I am new to NextJS. I have a page that needs to display real-time data pulled from a Hasura GraphQL backend.
In other non-NextJS apps, I have used GraphQL subscriptions with the Apollo client library. Under the hood, this uses websockets.
I can get GraphQL working in NextJS when it's not using subscriptions. I'm pretty sure this is running on the server-side:
import React from "react";
import { AppProps } from "next/app";
import withApollo from 'next-with-apollo';
import { ApolloProvider } from '@apollo/react-hooks';
import ApolloClient, { InMemoryCache } from 'apollo-boost';
import { getToken } from "../util/auth";
interface Props extends AppProps {
apollo: any
}
const App: React.FC<Props> = ({ Component, pageProps, apollo }) => (
<ApolloProvider client={apollo}>
<Component {...pageProps}/>
</ApolloProvider>
);
export default withApollo(({ initialState }) => new ApolloClient({
uri: "https://my_hasura_instance.com/v1/graphql",
cache: new InMemoryCache().restore(initialState || {}),
request: (operation: any) => {
const token = getToken();
operation.setContext({
headers: {
authorization: token ? `Bearer ${token}` : ''
}
});
}
}))(App);
And I use it this way:
import { useQuery } from '@apollo/react-hooks';
import { gql } from 'apollo-boost';
const myQuery = gql`
query {
...
}
`;
const MyComponent: React.FC = () => {
const { data } = useQuery(myQuery);
return <p>{JSON.stringify(data)}</p>
}
However, I would instead like to do this:
import { useSubscription } from '@apollo/react-hooks';
import { gql } from 'apollo-boost';
const myQuery = gql`
subscription {
...
}
`;
const MyComponent: React.FC = () => {
const { data } = useSubscription(myQuery);
return <p>{JSON.stringify(data)}</p>
}
What I've tried
I've tried splitting the HttpLink and WebsocketLink elements in the ApolloClient, like so:
import React from "react";
import { AppProps } from "next/app";
import { ApolloProvider } from '@apollo/react-hooks';
import withApollo from 'next-with-apollo';
import { InMemoryCache } from "apollo-cache-inmemory";
import ApolloClient from "apollo-client";
import { split } from 'apollo-link';
import { HttpLink } from 'apollo-link-http';
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';
import { getToken } from "../util/auth";
interface Props extends AppProps {
apollo: any
}
const App: React.FC<Props> = ({ Component, pageProps, apollo }) => (
<ApolloProvider client={apollo}>
<Component {...pageProps}/>
</ApolloProvider>
);
const wsLink = new WebSocketLink({
uri: "wss://my_hasura_instance.com/v1/graphql",
options: {
reconnect: true,
timeout: 10000,
connectionParams: () => ({
headers: {
authorization: getToken() ? `Bearer ${getToken()}` : ""
}
})
},
});
const httpLink = new HttpLink({
uri: "https://hasura-g3uc.onrender.com/v1/graphql",
});
const link = process.browser ? split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink
) : httpLink;
export default withApollo(({ initialState }) => new ApolloClient({
link: link,
cache: new InMemoryCache().restore(initialState || {}),
}))(App);
But when I load the page, I get an Internal Server Error
, and this error in the terminal:
Error: Unable to find native implementation, or alternative implementation for WebSocket!
It seems to me that the ApolloClient
is then being generated on the server-side, where there is no WebSocket implementation. How can I make this happen on the client-side?