NodeJS GraphQL: Adding Realtime GraphQL Subscriptions in NodeJS and Express #7

In this post, you will learn how to add realtime functionality into your app with GraphQL subscriptions.



In this section, you’ll learn how you can bring realtime functionality into your app by implementing GraphQL subscriptions. In this post we will learn:
  • How to implement GraphQL Subscriptions in nodejs server for realtime updates
  • Send realtime updates to subscribed clients when a new Human character is created
  • Send realtime updates to subscribed clients

So lets get started !
. . .

What are GraphQL subscriptions?

Subscriptions are a GraphQL feature that allows a server to send data to its clients when a specific event happens. Subscriptions are usually implemented with WebSockets. In that setup, the server maintains a steady connection to its subscribed client. This also breaks the “Request-Response-Cycle” that was used for all previous interactions with the API. Instead, the client initially opens up a long-lived connection to the server by sending a subscription query that specifies which event it is interested in. Every time this particular event happens, the server uses the connection to push the event data to the subscribed client(s).
. . .

Adding Subscriptions In NodeJS

Subscriptions depend on the use of a publish and subscribe primitive to generate the events that notify a subscription. PubSub is a factory that creates event generators that are provided by all supported packages.

PubSub is an implementation of the PubSubEngine interface, which has been adopted by a variety of additional event-generating backends.
const { PubSub } = require('apollo-server');
const pubsub = new PubSub();
Subscriptions are another root level type, similar to Query and Mutation. To start, we need to add the Subscription type to our schema:

// Construct a schema, using GraphQL schema language const typeDefs = gql` type Person { ... } type Query { ... } type Mutation { ... } type Subscription { newPerson: Person } `;

Inside our resolver map, we add a Subscription resolver that returns an AsyncIterator, which listens to the events asynchronously.

To generate events in the example, we notified the pubsub implementation inside of our Mutation resolver with publish. This publish call can occur outside of a resolver if required.

const { PubSub } = require('apollo-server');
// #1 const pubsub = new PubSub(); let Persons = [ ... ]; const PERSON_ADDED = 'PERSON_ADDED'; // Provide resolver functions for your schema fields const resolvers = {
// #2 Subscription: { newPerson: { // Additional event labels can be passed to asyncIterator creation // #3
subscribe: () => pubsub.asyncIterator([PERSON_ADDED]), }, }, Query: { ... }, Mutation: { ... } };

Let's go through the comments
  1. First, we have created a pub-sub interface, we are using in-memory pub-sub implementation provided by Apollo Server.
  2. In our resolvers, we have added the new subscription resolver that resolves the subscriptions.
  3. When a user subscribes to newPerson it will return the async iterator, a message will be pushed to the user when new person is created
. . .

Updating Express server

we already have an existing Express HTTP server (created with createServer), you can add subscriptions on a specific path.
For example: if your server is already running on port 4000 and accepts GraphQL HTTP connections (POST) on the /graphql endpoint, you can expose /graphql as your WebSocket subscriptions endpoint.

We need to update our existing server to support the Websocket connection for incoming graphql subscriptions requests.
const express = require('express'); const { ApolloServer } = require('apollo-server-express'); const http = require("http"); const typeDefs = require('./typeDefs'); const resolvers = require('./resolvers');
// #1 const server = new ApolloServer({ typeDefs, resolvers,
// #2 subscriptions: { onConnect: () => console.log('Connected to websocket'), }, }); const app = express(); server.applyMiddleware({ app }); const httpServer = http.createServer(app);
// #3 server.installSubscriptionHandlers(httpServer); httpServer.listen(4000, () => { console.log(`🚀 Server ready at http://localhost:${4000}${server.graphqlPath}`) console.log(`🚀 Subscriptions ready at ws://localhost:${4000}${server.subscriptionsPath}`) })

Let's go through the comments
  1. First we have created the Apollo Srevre instance
  2. Its takes subscriptions object as parameters where you can listen for websocket events
  3. With an existing HTTP server (created withcreateServer), we can add subscriptions using theinstallSubscriptionHandlers.

With all the code in place, it’s time to test your realtime API ⚡️ You can do so, by using two instances (i.e. windows) of the GraphQL Playground at once.
If you haven’t already, restart the server by first killing it with CTRL+C, then run go run node index.js

Run following query in graphql playground to subscribe to the new person
subscription {
newPerson {
id
name
mass
height
homeworld
}
}

Now we have a subscription listening for a new message. We need to publish the message in order to notify the subscribed clients. We will use the pubsub interface that we have created in above code

We will publish the new message to clients whenever a new person is created, we need to update the createPerson as follows

const { PubSub } = require('apollo-server');
// #1 const pubsub = new PubSub(); let Persons = [ ... ];
// #2 const PERSON_ADDED = 'PERSON_ADDED'; // Provide resolver functions for your schema fields const resolvers = { Subscription: { newPerson: { // Additional event labels can be passed to asyncIterator creation subscribe: () => pubsub.asyncIterator([PERSON_ADDED]), }, }, Query: { ... }, Mutation: { async createPerson(parent, args ) { const newPerson = { id: args.id, name: args.name, gender: args.gender, height: args.height, mass: args.mass, homeworld: args.homeworld }; Persons.push(newPerson);

// #3 pubsub.publish(PERSON_ADDED, { newPerson: newPerson }); return newPerson; }, ... };

Let's walk through the comments
  1. PubSub interface to publish messages
  2. We have defied the notification type const
  3. Whenever a new person is created we are publishing a new message on pubsub interface with the data.
. . .

Testing Subscriptions

With all the code in place, it’s time to test your realtime API ⚡️ You can do so, by using two instances (i.e. windows) of the GraphQL Playground at once.
If you haven’t already, restart the server by first killing it with CTRL+C, then run go run main.go again.
Next, open two browser windows and navigate both to the endpoint of your GraphQL server: http://localhost:4000.

As you might guess, you’ll use one Playground to send a subscription query and thereby create a permanent WebSocket connection to the server. The second one will be used to send a post mutation that triggers the subscription.
In one Playground, send the following subscription:
subscription { newPerson { id name mass height homeworld } }

In contrast to what happens when sending queries and mutations, you’ll not immediately see the result of the operation. Instead, there’s a loading spinner indicating that it’s waiting for an event to happen.

Time to trigger a subscription event. Open the second playground and end the following post mutation inside a GraphQL Playground.
mutation {
createPerson(
id: 10000,
name: "Mike",
height: 100,
homeworld: "enarth",
mass: 100,
gender: "UNKNOWN",
) {
id
name
mass
height
homeworld
}
}

Now observe the Playground where the subscription was running:

We have received the new person characters as we created a new person with the mutation.
So, this brings us to the end of realtime graphql subscriptions into the nodejs server using the WebSockets. Happy hacking!!
. . .


Never miss a post from Gufran Mirza, when you sign up for Ednsquare.