hrushilok
hrushilok

Reputation: 455

I would like to know a working exsample of graphql subscriptions

Recently they have deprecated the subscriptionManager. I would like to know how to setup resolvers, define subscribe and execute function.

Upvotes: 2

Views: 1320

Answers (1)

Michael Brook
Michael Brook

Reputation: 380

You will need to upgrade to Apollo 2.0. I have recently done a write-up on how to use Apollo 2.0 since the official docs have not yet been updated.

In short, you have to use apollo-link now on the client and execute and subscribe from the graphql package now get passed directly to SubscriptionServer instead.

You will first need the right packages with the right versions:

npm install --save apollo-client@beta apollo-cache-inmemory@beta [email protected] [email protected] [email protected] graphql-subscriptions subscriptions-transport-ws apollo-server-express express graphql graphql-tools body-parser

If you're running Meteor, you might also need:

meteor add apollo swydo:blaze-apollo swydo:graphql webapp

Now for the code, the following was made in Meteor, but it can easily adapt to other server types, like Express. You can also download a working example app.

On the client:

import { ApolloClient } from 'apollo-client';
import { ApolloLink } from 'apollo-link';
import { HttpLink } from 'apollo-link-http';
import WebSocketLink from 'apollo-link-ws';
import Cache from 'apollo-cache-inmemory';
import { getOperationAST } from 'graphql';

const httpUri = 'http://localhost:3000/graphql';
const wsUri = 'ws://localhost:3000/subscriptions';

const link = ApolloLink.split(
  operation => {
    const operationAST = getOperationAST(operation.query, operation.operationName);
    return !!operationAST && operationAST.operation === 'subscription';
  },
  new WebSocketLink({
    uri: wsUri,
    options: {
      reconnect: true, //auto-reconnect
      // // carry login state (should use secure websockets (wss) when using this)
      // connectionParams: {
      //   authToken: localStorage.getItem("Meteor.loginToken")
      // }
    }
  }),
  new HttpLink({ uri: httpUri })
);

const cache = new Cache(window.__APOLLO_STATE);

const client = new ApolloClient({
  link,
  cache
});

On the server:

import { WebApp } from 'meteor/webapp'; // Meteor-specific
import { execute, subscribe } from 'graphql';
import { SubscriptionServer } from 'subscriptions-transport-ws';
import { createApolloServer, addCurrentUserToContext } from 'meteor/apollo'; // specific to Meteor, but you can always check out the Express implementation
import { makeExecutableSchema } from 'graphql-tools';
import resolvers from './resolvers'; // your custom resolvers
import typeDefs from './schema.graphql'; // your custom schema

// make schema executable
const schema = makeExecutableSchema({
  typeDefs,
  resolvers
});

// any additional context you use for your resolvers, if any
const context = {};

// start a graphql server with Express handling a possible Meteor current user
// if you're not using Meteor, check out https://github.com/apollographql/apollo-server for instructions on how to create a server in pure Node
createApolloServer({ 
  schema,
  context
}, {
  // // enable access to GraphQL API cross-domain (requires NPM "cors" package)
  // configServer: expressServer => expressServer.use(cors())
});

// create subscription server
// non-Meteor implementation here: https://github.com/apollographql/subscriptions-transport-ws
new SubscriptionServer({
  schema,
  execute,
  subscribe,
  // // on connect subscription lifecycle event
  // onConnect: async (connectionParams, webSocket) => {
  //   // if a meteor login token is passed to the connection params from the client, 
  //   // add the current user to the subscription context
  //   const subscriptionContext = connectionParams.authToken
  //     ? await addCurrentUserToContext(context, connectionParams.authToken)
  //     : context;
  //   return subscriptionContext;
  // }
}, {
  server: WebApp.httpServer,
  path: '/subscriptions'
});

resolvers.js

import { withFilter } from 'graphql-subscriptions'; // will narrow down the changes subscriptions listen to
import { PubSub } from 'graphql-subscriptions';
import { People } from '../imports/api/collections'; // Meteor-specific for doing database queries

const pubsub = new PubSub();

const resolvers = {

  Query: {
    person(obj, args, context) {
      const person = People.findOne(args.id);
      if (person) {
        // Mongo stores id as _id, but our GraphQL API calls for id, so make it conform to the API
        person.id = person._id;
        delete person._id;
      }
      return person;
    }
  },

  Mutation: {
    updatePerson(obj, args, context) {
      // You'll probably want to validate the args first in production, and possibly check user credentials using context
      People.update({ _id: args.id }, { $set: { name: args.name, eyeColor: args.eyeColor, occupation: args.occupation } });
      pubsub.publish("personUpdated", { personUpdated: args }); // trigger a change to all subscriptions to this person
      // Note: You must publish the object with the subscription name nested in the object!
      // See: https://github.com/apollographql/graphql-subscriptions/issues/51
      return args;
    }
  },

  Subscription: {
    personUpdated: {
      // See: https://github.com/apollographql/graphql-subscriptions#channels-mapping
      // Take a look at "Channels Mapping" for handling multiple create, update, delete events
      // Also, check out "PubSub Implementations" for using Redis instead of PubSub
      // PubSub is not recommended for production because it won't work if you have multiple servers
      // withFilter makes it so you can only listen to changes to this person instead of all people
      subscribe: withFilter(() => pubsub.asyncIterator('personUpdated'), (payload, args) => {
        return (payload.personUpdated.id===args.id);
      })
    }
  }

};

export default resolvers;

schema.graphql

enum EyeColor {
  brown
  blue
  green
  hazel
}

type Person {
  id: ID
  name: String
  eyeColor: EyeColor
  occupation: String
}

type Query {
  person(id: ID!): Person
}

type Mutation {
  updatePerson(id: ID!, name: String!, eyeColor: EyeColor!, occupation: String!): Person
}

type Subscription {
    personUpdated(id: ID!): Person
}

schema {
    query: Query
    mutation: Mutation
    subscription: Subscription
}

A full write-up about this can be found in this Medium post: How to get Apollo 2.0 working with GraphQL + subscriptions.

An example app demonstrating how to use Apollo 2.0 with a GraphQL server + subscriptions can be found here: meteor-apollo2

Upvotes: 4

Related Questions