How to connect apollo client to prisma subscriptions?

prisma

#1

Hey folks!

So I’ve followed the example in the tutorial:

And have successfully set up subscriptions using the GraphQL Playground.

Here’s some server side subscription code I have:

const Subscription = {
  messages: {
    subscribe(parent, args, ctx, info) {
      return ctx.db.subscription.message(
        {
          where: {
            mutation_in: ['CREATED', 'UPDATED'],
          },
        },
        info,
      );
    },
  },
};

module.exports = Subscription;

And here is my server:

const { GraphQLServer } = require('graphql-yoga');

const Mutation = require('./resolvers/Mutation');
const Query = require('./resolvers/Query');
const Conversation = require('./resolvers/Conversation');
const Message = require('./resolvers/Message');
const Subscription = require('./resolvers/Subscription');

const db = require('./db');

// Create GraphQL Yoga server
// eslint-disable
function createServer() {
  return new GraphQLServer({
    typeDefs: 'src/schema.graphql',
    resolvers: {
      Mutation,
      Query,
      Subscription,
      Conversation,
      Message,
    },
    resolverValidationOptions: {
      requireResolversForResolveType: false,
    },
    context: req => ({ ...req, db }),
  });
}

module.exports = createServer;

However, when I then try and connect to apollo client using their tutorial:

It gives the following error:
Unable to find native implementation, or alternative implementation for WebSocket!

The tutorial even says to install the subscriptions-transport-ws package, but does not give any insight into where to use it.

Does anyone have any suggestions on what a client should look like, possibly with a subscription component example?


#2

Does this help?
https://akryum.github.io/vue-apollo/guide/apollo/subscriptions.html#setup
Although it is based on Vue, it works for me. How did you setup the ApolloClient?


#4

I appreciate the help @Elfayer!

Ok, so I’ve since updated my client to use the apollo-client package, and tried to implement web sockets…but no luck.

import withApollo from 'next-with-apollo';
import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { ApolloLink, Observable, split } from 'apollo-link';
import { onError } from 'apollo-link-error';
import { withClientState } from 'apollo-link-state';
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';

import { endpoint, prodEndpoint, wsEndpoint } from '../config';
import { LOCAL_STATE_QUERY } from '../components/Modal';


import introspectionQueryResultData from './fragmentTypes.json';

function createClient({ headers }) {
  const fragmentMatcher = new IntrospectionFragmentMatcher({
    introspectionQueryResultData,
  });

  const myCache = new InMemoryCache({ fragmentMatcher });

  const request = async (operation) => {
    operation.setContext({
      fetchOptions: {
        credentials: 'include',
      },
      headers,
    });
  };

  const requestLink = new ApolloLink((operation, forward) =>
    new Observable(observer => {
      let handle;
      Promise.resolve(operation)
        .then(oper => request(oper))
        .then(() => {
          handle = forward(operation).subscribe({
            next: observer.next.bind(observer),
            error: observer.error.bind(observer),
            complete: observer.complete.bind(observer),
          });
        })
        .catch(observer.error.bind(observer));

      return () => {
        if (handle) handle.unsubscribe();
      };
    })
  );

  const httpLink = new HttpLink({
    uri: process.env.NODE_ENV === 'development' ? endpoint : prodEndpoint,
    credentials: 'include',
  });


  // Create a WebSocket link:
  const wsLink = new WebSocketLink({
    uri: wsEndpoint,
    options: {
      reconnect: true,
    },
  });

  // using the ability to split links, you can send data to each link
  // depending on what kind of operation is being sent
  const networkLink = split(
    // split based on operation type
    ({ query }) => {
      const { kind, operation } = getMainDefinition(query);
      return kind === 'OperationDefinition' && operation === 'subscription';
    },
    wsLink,
    httpLink,
  );

  return new ApolloClient({
    link: ApolloLink.from([
      networkLink,
      onError(({ graphQLErrors, networkError }) => {
        if (graphQLErrors) {
          // sendToLoggingService(graphQLErrors);
          console.log('here is some error');
        }
        if (networkError) {
          console.log('there is a network error');
          // logoutUser();
        }
      }),
      requestLink,
      withClientState({
        defaults: {
          modalOpen: false,
        },
        resolvers: {
          Mutation: {
            toggleModal(_, variables, { cache }) {
              // read modal
              const { modalOpen } = cache.readQuery({
                query: LOCAL_STATE_QUERY,
              });
              // write modal state
              const data = { data: { modalOpen: !modalOpen } };
              cache.writeData(data);
              return data;
            },
          },
        },
        cache: myCache,
      }),
    ]),
    cache: myCache,
  });
}


export default withApollo(createClient);

But now I just get the following error:
Error: Unable to find native implementation, or alternative implementation for WebSocket!


#5

Ok! So I think I’ve figured out the config for clients. I had to add a process.browser ternary because I’m using next.js

Here’s what it looks like now:

import withApollo from 'next-with-apollo';
import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { ApolloLink, Observable, split } from 'apollo-link';
import { onError } from 'apollo-link-error';
import { withClientState } from 'apollo-link-state';

import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';

import { endpoint, prodEndpoint, wsEndpoint } from '../config';
import { LOCAL_STATE_QUERY } from '../components/Modal';

import introspectionQueryResultData from './fragmentTypes.json';

function createClient({ headers }) {
  const fragmentMatcher = new IntrospectionFragmentMatcher({
    introspectionQueryResultData,
  });

  // const networkLink = new HttpLink({
  const httpLink = new HttpLink({
    uri: process.env.NODE_ENV === 'development' ? endpoint : prodEndpoint,
    credentials: 'include',
  });

  // Create a WebSocket link:
  const wsLink = process.browser ? new WebSocketLink({
    uri: wsEndpoint,
    options: {
      reconnect: true,
    },
  }) : () => console.log('SSR');
  // using the ability to split links, you can send data to each link
  // depending on what kind of operation is being sent
  const networkLink = split(
    // split based on operation type
    ({ query }) => {
      const { kind, operation } = getMainDefinition(query);
      return kind === 'OperationDefinition' && operation === 'subscription';
    },
    wsLink,
    httpLink,
  );

  const myCache = new InMemoryCache({ fragmentMatcher });

  const request = async (operation) => {
    operation.setContext({
      fetchOptions: {
        credentials: 'include',
      },
      headers,
    });
  };

  const requestLink = new ApolloLink((operation, forward) =>
    new Observable(observer => {
      let handle;
      Promise.resolve(operation)
        .then(oper => request(oper))
        .then(() => {
          handle = forward(operation).subscribe({
            next: observer.next.bind(observer),
            error: observer.error.bind(observer),
            complete: observer.complete.bind(observer),
          });
        })
        .catch(observer.error.bind(observer));

      return () => {
        if (handle) handle.unsubscribe();
      };
    })
  );

  return new ApolloClient({
    name: 'web',
    link: ApolloLink.from([
      onError(({ graphQLErrors, networkError }) => {
        if (graphQLErrors) {
          // sendToLoggingService(graphQLErrors);
          console.log('here is some error');
        }
        if (networkError) {
          console.log('there is a network error');
          // logoutUser();
        }
      }),
      requestLink,
      withClientState({
        defaults: {
          modalOpen: false,
        },
        resolvers: {
          Mutation: {
            toggleModal(_, variables, { cache }) {
              // read modal
              const { modalOpen } = cache.readQuery({
                query: LOCAL_STATE_QUERY,
              });
              // write modal state
              const data = { data: { modalOpen: !modalOpen } };
              cache.writeData(data);
              return data;
            },
          },
        },
        cache: myCache,
      }),
      networkLink,
    ]),
    cache: myCache,
    ssrMode: true,
  });
}


export default withApollo(createClient);

I’m still having trouble getting the actual subscription logic to work, but that’ll be another post


#6

I appreciate the help @Elfayer!!!