Nextjs urql subscription exchange import problem

620 Views Asked by At

i can't get urql subscriptions to work with NextJS because of imports problem.

Basically i'm using this graphql-ws lib that is recommended in urql docs, that needs ws implementation library (eg: 'ws'). And when i import WebSocket from 'ws' i get this error: Module not found: Can't resolve 'net'

import { createClient, defaultExchanges, subscriptionExchange, Client } from 'urql';
import { createClient as createWSClient } from 'graphql-ws';
import WebSocket from 'ws'; // <-- This causes the error

export const createUrqlClient = (): Client => {
  const wsClient = createWSClient({
    url: 'ws://xxx/graphql',
    webSocketImpl: WebSocket,
  });

  const client = createClient({
    url: 'http://xxx/graphql',
    exchanges: [
      ...defaultExchanges,
      subscriptionExchange({
        forwardSubscription: operation => ({
          subscribe: sink => ({
            unsubscribe: wsClient.subscribe(operation, sink),
          }),
        }),
      }),
    ],
  });

  return client;
};

I tried nextjs dynamic import and both of these didn't work as well:

const WebSocket = dynamic(() => import('ws'), { ssr: false });
const WebSocket = dynamic(() => import('ws').then(module => module.default), { ssr: false });

I also tried to alter webpack config in next.config.js to not bundle these libs at all:

webpack: (config, { isServer }) => {
  if (!isServer) {
    config.resolve.fallback = {
      child_process: false,
      process: false,
      fs: false,
      util: false,
      http: false,
      https: false,
      tls: false,
      net: false,
      crypto: false,
      path: false,
      os: false,
      stream: false,
      zlib: false,
      querystring: false,
      events: false,
      'utf-8-validate': false,
      bufferutil: false,
    };
  }
  return config;
},

but then i get these errors:

./node_modules/ws/lib/validation.js
Module not found: Can't resolve 'utf-8-validate' in '/home/danko/app/node_modules/ws/lib'
warn  - ./node_modules/ws/lib/buffer-util.js
Module not found: Can't resolve 'bufferutil' in '/home/danko/app/node_modules/ws/lib'

if i add 'utf-8-validate': false and bufferutil: false to the cfg as well i get this err:

TypeError: Class extends value undefined is not a constructor or null

So basically nothing works properly then as you can see...

How hard can this be, i can't be the only person that uses urql subscriptions with nextjs, hope somebody can help me with this. Thanks!

2

There are 2 best solutions below

0
On BEST ANSWER

Basically as i thought, impl was not needed because native html5 websocket can be used, problem was trash nextjs with it's server side thing.

I pretty much don't use that exchange when typeof window !== 'undefined' this is the working code:

import { createClient, dedupExchange, cacheExchange, subscriptionExchange, Client, Exchange } from 'urql';
import { multipartFetchExchange } from '@urql/exchange-multipart-fetch';
import { createClient as createWSClient } from 'graphql-ws';

export const createUrqlClient = (): Client => {
  const graphqlEndpoint = process.env!.NEXT_PUBLIC_GRAPHQL_ENDPOINT as string;
  const graphqlWebsocketEndpoint = process.env!.NEXT_PUBLIC_GRAPHQL_WS_ENDPOINT as string;

  let exchanges: Exchange[] | undefined = [dedupExchange, cacheExchange, multipartFetchExchange];

  if (typeof window !== 'undefined') {
    const wsClient = createWSClient({
      url: graphqlWebsocketEndpoint,
    });

    const subExchange = subscriptionExchange({
      forwardSubscription: operation => ({
        subscribe: sink => ({
          unsubscribe: wsClient.subscribe(operation, sink),
        }),
      }),
    });

    exchanges.push(subExchange);
  }

  const client = createClient({
    url: graphqlEndpoint,
    requestPolicy: 'cache-and-network',
    exchanges,
    fetchOptions: () => ({
      credentials: 'include',
    }),
  });

  return client;
};

0
On

@dankobgd

I had the same problem and your answer helped me. I simplified a bit and it's working

export const client = (): Client => {
  let exchanges: Exchange[] = [...defaultExchanges]

  if (typeof window !== 'undefined') {
    const wsClient = createWSClient({
      url: wsUrl,
    })

    const subExchange = subscriptionExchange({
      forwardSubscription: (operation) => ({
        subscribe: (sink) => ({
          unsubscribe: wsClient.subscribe(operation, sink),
        }),
      }),
    })
    exchanges.push(subExchange)
  }

  return createClient({
    url,
    exchanges,
  })
}