Search code examples
node.jsnext.jsapollo-clientgraphql-subscriptions

Apollo subscriptions - Nextjs - Error: Observable cancelled prematurely at Concast.removeObserver


I am trying to use apollo/graphql subscription in my nextjs project, my graphql server is placed in external nextjs service,I can work with queries and mutation without any problem but when I use an implementation of useSubscription I get the following error:

"Error: Observable cancelled prematurely at Concast.removeObserver (webpack-internal:///../../node_modules/@apollo/client/utilities/observables/Concast.js:118:33) at eval (webpack-internal:///../../node_modules/@apollo/client/utilities/observables/Concast.js:21:47) at cleanupSubscription (webpack-internal:///../../node_modules/zen-observable-ts/module.js:92:7) at Subscription.unsubscribe (webpack-internal:///../../node_modules/zen-observable-ts/module.js:207:7) at cleanupSubscription (webpack-internal:///../../node_modules/zen-observable-ts/module.js:97:21) at Subscription.unsubscribe (webpack-internal:///../../node_modules/zen-observable-ts/module.js:207:7) at eval (webpack-internal:///../../node_modules/@apollo/client/react/hooks/useSubscription.js:106:26) at safelyCallDestroy (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:22763:5) at commitHookEffectListUnmount (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:22927:11) at invokePassiveEffectUnmountInDEV (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:24998:13) at invokeEffectsInDev (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:27137:11) at commitDoubleInvokeEffectsInDEV (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:27110:7) at flushPassiveEffectsImpl (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:26860:5) at flushPassiveEffects (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:26796:14) at eval (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:26592:9) at workLoop (webpack-internal:///../../node_modules/scheduler/cjs/scheduler.development.js:266:34) at flushWork (webpack-internal:///../../node_modules/scheduler/cjs/scheduler.development.js:239:14) at MessagePort.performWorkUntilDeadline (webpack-internal:///../../node_modules/scheduler/cjs/scheduler.development.js:533:21)"

I know that the subscriptions server is working right because I can to listening from apollo studio and I have created a spa with create-react-app and it works fine

I have used:

Server:

  • "apollo-server-express": "^3.6.7"
  • "graphql-ws": "^5.7.0"

Client

  • "next": "^12.1.5"
  • "@apollo/client": "^3.5.10"
  • "graphql-ws": "^5.7.0"

Hook implementation

const room = useSubscription(
    gql`
      subscription onRoomAdded($roomAddedId: ID!) {
        roomAdded(id: $roomAddedId) {
          id
          name
        }
      }
    `
  );

Client implementation

import { ApolloClient, HttpLink, InMemoryCache, split } from '@apollo/client';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { getMainDefinition } from '@apollo/client/utilities';
import { createClient } from 'graphql-ws';
import fetch from 'isomorphic-fetch';

const HOST = 'http://localhost:3001/graphql';
const HOST_WS = 'ws://localhost:3001/graphql';
const isServer = typeof window === 'undefined';

if (isServer) {
  global.fetch = fetch;
}

const httpLink = new HttpLink({
  uri: HOST,
});

const link = isServer
  ? httpLink
  : split(
      ({ query }) => {
        const definition = getMainDefinition(query);
        return (
          definition.kind === 'OperationDefinition' &&
          definition.operation === 'subscription'
        );
      },
      new GraphQLWsLink(
        createClient({
          url: HOST_WS,
        })
      ),
      httpLink
    );

const client = new ApolloClient({
  ssrMode: isServer,
  link,
  cache: new InMemoryCache(),
});

export default client;

any idea about the problem? I think the problem could be that NextJS only works with subscriptions-transport-ws but in the official apollo documentation indicates that the new official way is to use graphql-ws the other library is unmaintained already

UPDATE! I have checked that the subscriptions are working right in production build, I'm investigating how to implement in development process. any suggestions are welcome.


Solution

  • If it is working in production, but in not in dev, you may have the same issue I had with my React SPA: StrictMode and double rendering as described in this github issue. So far I have found 2 ways to make it work:

    1. remove StrictMode
    2. subscribe with vanilla JS instead ofuseSubscription
    const ON_USER_ADDED = gql`
      subscription OnUserAdded {
        userAdded {
          name
          id
        }
      }
    `;
    
    const subscribe = () => {
    client.subscribe({
      query: ON_USER_ADDED,
    }).subscribe({
        next(data) {
          console.log('data', data);
        },
        complete(){
          console.log('complete');
        },
        error(err) {
          console.log('error', err);
        }
      })
    };