Jan 15, 2018

GraphQL: Server-side Subscriptions

OK, it’s 2k18 and everyone has already heard about GraphQL at least once. Maybe from your colleagues or even from some gopnik guys from the unfavourable areas of your city. GraphQL hype is everywhere.

Senior JavaScript developers discussing new development paradigms

In a few words, GraphQL is a query language for your API created by Facebook in 2012 and publicly released in 2015. It describes the capabilities and requirements by using a type system you define for your data. Clear? Sure, all these queries and mutations, even your grandpa already knows about and uses them. That’s why we won’t go into detail on the basics. Instead, we’ll start with adding Subscriptions to your existing server app.

But what are GraphQL Subscriptions? Subscriptions are a fairly new GraphQL feature and although many companies already use them in a production environment, they’re still not in the GraphQL specification. Subscriptions allow realtime updates about data changes in a standard GraphQL syntax. To set them, all you need to do is to subscribe to specific mutations, then after a mutation is triggered, execute some code in your app to react to that change.

If you are interested in more details I recommend this talk by Uri Goldstein from the ReactiveConf I attended few months ago in Bratislava, Slovakia.

No more theory, let’s jump right into practice and build a simple and scalable GraphQL Subscriptions server using Node.js framework Express, MongoDB as a database and Flow.js for static type checking.


Let’s begin with cloning the git repo and installing the necessary dependencies.

git clone https://github.com/nezdemkovski/graphql-subscriptions-server.git
cd graphql-subscriptions-server
cd yarn install

I’ve assumed that you have MongoDB already installed and running. By default it runs on the27017 port so we will use the same port for our app.

As an interactive in-browser GraphQL IDE we are going to use Playground brought to you by our pals from Graphcool. It provides automatic schema reloading and better support for GraphQL Subscriptions, which is critical in our case.

So let’s start our server.

yarn start

When you navigate your browser to http://localhost:8080/playground, you should enter the Playground:

Playground is so sexy, you’ll immediately forget about GraphiQL

But before playing with it, let’s highlight what we need to set up a GraphQL server with subscriptions.

Add subscriptions support to our GraphQL server through WebSockets

Since we can’t push frequent updates from the server to the client over HTTP, we need a WebSocket server. To create it we’re going to use the subscriptions-transport-ws package, which makes this quite simple. Add the necessary import statements in src/index.js

import { createServer } from 'http';
import { execute, subscribe } from 'graphql';
import { SubscriptionServer } from 'subscriptions-transport-ws';

To open WebSocket on the GraphQL server, we have to wrap the Express server with createServer.

const app = express();
const server = createServer(app);

Now let’s use the wrapped server to set up a WebSocket to listen to GraphQL subscriptions.

server.listen(PORT, () => {
new SubscriptionServer(
{
execute,
subscribe,
schema: Schema,
},
{
server: server,
path: '/subscriptions',
},
);console.log(`GraphQL Server is now running on ${BASE_URI}`);
console.log(`Subscriptions are running on ${WS_BASE_URI}/subscriptions`);
});

The last thing in this step is to configure GraphQL Playground to use the subscriptions WebSocket we just set up. We use it as a server middleware and thegraphql-playground-middleware-express package is all we need. If you use another server framework (such as Hapi or Koa), you’ll find the proper packages here as well.

Import desired package:

import expressPlayground from 'graphql-playground-middleware-express';

And adjust the endpoints:

app.get(
'/playground',
expressPlayground({
endpoint: '/graphql',
subscriptionsEndpoint: `${WS_BASE_URI}/subscriptions`,
}),
);

Declaring subscriptions in the schema and adding a subscription Resolver

Now let’s define subscriptions in our GraphQL schema.

// RootSubscriptions.jsimport { GraphQLObjectType } from 'graphql';import NewCat from './NewCat';
import RemovedCat from './RemovedCat';export default new GraphQLObjectType({
name: 'RootSubscription',
description: 'Root Subscription',
fields: {
newCat: NewCat,
removedCat: RemovedCat,
},
});

Right after that we’ll construct an instance of PubSub to handle the subscription topics for our application.

// serverConfig.jsimport { PubSub } from 'graphql-subscriptions';export const pubsub = new PubSub();

Creating a subscription resolver is very similar to queries and mutations. The only difference is that instead of passing only the resolve function, we also pass object with the subscribe method, which should return the asyncIterator function of the PubSub.

// newCat.jsimport { pubsub } from '../serverConfig';
import GraphQLCat from '../outputs/Cat';import type { Cat } from '../types/Cat';export default {
type: GraphQLCat,
subscribe: () => pubsub.asyncIterator('newCat'),
resolve: (payload: Cat) => payload,
};

As soon as our subscription resolver is ready, we can start publishing newCat into it. For that we just call thePubSub method pubsub.publish('newCat', cat) inside theaddCat mutation.

// addCat.jsexport default {
...
resolve: async (
_: mixed,
args: argsType,
{ Cat }: GraphqlContextType,
): Promise<CatType> => {
const payload = {
...args,
createdAt: new Date().toISOString(),
};
const cat = await new Cat(payload).save();

pubsub.publish('newCat', cat);
return cat;
},
};

Let’s get back to the browser and finally try it out by running the following query:

subscription newCat {
newCat {
id
name
nickName
description
createdAt
avatarUrl
age
}
}

Playground is now listening for the creation of a new Cat and as soon as theaddCat mutation is triggered, you should see it in your Playground window!

mutation addCat {
addCat(name: "Pussie Cat", nickName: "pussie", description: "Ooops, I killed a mouse", avatarUrl: "http://tachyons.io/img/cat-720.jpg", age: 5) {
id
name
nickName
description
createdAt
avatarUrl
age
}
}

Demo time

Congratulations! You have implemented your first server-side GraphQL Subscriptions through WebSockets! You can find the complete source of this example on my GitHub.

Cats are happy.


Liked the post and want to start using GraphQL on a daily basis? At the moment we’re looking for experienced JavaScript geeks to join us at Kiwi.com in the one of the most beautiful cities in Europe — Prague.

Search
Share
Featured articles
Don’t Fix Bad Data, Do This Instead
The Relevance Of Tech Conferences In A Post Pandemic World