GraphQL subscriptions with Node.js
Tags: graphql, node, javascript
Published: 2020-01-14 17:56:03

---
title: GraphQL subscriptions with Node.js
published: true
date: 2020-01-09 14:00:58 UTC
tags: graphql,node,javascript 
canonical_url: https://blog.logrocket.com/graphql-subscriptions-with-node-js/
cover_image: https://thepracticaldev.s3.amazonaws.com/i/57cs3sij05p323v5fb6z.png
---
**Written by [Deepak Gupta](https://blog.logrocket.com/author/deepakgupta/)**✏️

Ever wonder how Facebook notifies you when a friend posts something? Or how Google Maps updates your location in real time? The answer to these and many other modern mysteries is (among other things) GraphQL subscriptions.

![High-Level Overview of GraphQL Subscriptions](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2020/01/graphql-subscriptions-high-level-overview.png?resize=730%2C397&ssl=1)

_(Source: [GitHub](https://github.com/sangria-graphql/sangria-subscriptions-example))_

In this article, we’ll provide a basic understanding of GraphQL subscriptions for events on JSON data in a Node.js server.

Before we get started, you should have:

- Node.js installed
- A basic understanding of GraphQL concepts such as schema, query, mutation, and resolve

Without further ado, let’s dive in.

## What are GraphQL subscriptions?

GraphQL subscriptions enable you to subscribe to events under a source stream and receive notifications in real time via a response stream when a selected event executes. Once a GraphQL subscription is executed, a persistent function is created on the server that maps an underlying source stream to a returned response stream.

GraphQL subscriptions differ from queries in the way the data is delivered to the client. The latter immediately returns a single response, while the former returns a result every time data is published on a topic to which you have subscribed.

This is facilitated by a [publisher/subscriber mechanism](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) that can handle event-driven systems efficiently and at a scale. In a publisher/subscriber model, all messages and data flow according to the queue principle (first in, first out) and then to the subscriber.

![Diagram Illustrating the Query Principle in the Context of GraphQL subscriptions](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2020/01/graphql-subscriptions-query-principle-model.png?resize=730%2C383&ssl=1)

Note: for production, it is recommended to use the pub/sub implementation of Redis.

There are many packages available on `npm` that can be used to implement the pub/sub model for GraphQL subscriptions. Below are some of the most commonly used packages.

- [`graphql-yoga`](https://github.com/prisma-labs/graphql-yoga)  is a fully featured GraphQL server with focus on easy setup, performance, and a great developer experience
- [`graphql-subscriptions`](https://github.com/apollographql/graphql-subscriptions) lets you wire up GraphQL with a pub/sub system (such as Redis) to implement subscriptions in GraphQL
- [`apollo-server-express`](https://github.com/apollographql/apollo-server)  is the express and connect integration of GraphQL server. Apollo server is a community-maintained, open-source GraphQL server that works with many Node.js HTTP server frameworks

We will use the `graphql-yoga` module because it is built over the other two and provides all necessary dependency and server binding with Node.js under the hood. Don’t worry about those last two things; once you get a hang of the implementation, they will be a breeze.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## What we will code?

We will use the post data that is stored inside a JSON file, and we’ll perform the following operations.

- `getPosts`  (read all posts)
- `getPost` (read a specific post by ID)
- `updatePost`  (update a post)
- `deletePost`  (delete a post)
- `createPost`  (create a post)

Then, add the subscription to the last three operations.

Now it’s time to get our hands dirty with some code.

First, make a folder, name it whatever you like, and initialize it using Node.js.

```jsx
mkdir graphql-sub
cd graphql-sub
npm init
```

Next, install the dependency required.

```jsx
npm i --s graphql-yoga
```

Now we’ll create all our files.

```jsx
touch index.js postData.json typeDefs.js resolver.js
```

- `index.js` is responsible for the GraphQLServer creation with pub/sub, which we will see in a minute
- `postData.json` is the JSON file on which we will perform CRUD. Add the following [code](https://gist.github.com/dg92/78ce53985f4a647681da6fc7c7c9dab7) or an array of an object for a post who’s schema should be:
  - `id:ID!`
  - `title:String!`
  - `subtitle:String!`
  - `body:String!`
  - `published:Boolean!`
  - `author: String!`
  - `upvotes: Int!`
  - `downvotes: Int!`
  - `commentCount: Int!`
- `typeDefs.js` will be used to create schemas for the above operations
- `resolvers.js` will have the logic to resolve for all queries, mutation, and subscriptions defined under `typeDefs.js`

Inside `typeDefs.js`, add the following code.

```jsx
//type definitions and schemas - (operation and data structure)
const typeDefs = `
    type Query {
        getPosts(query: String):[Post!]!
        getPost(query: String):Post!
    }
    type Post{
        id:ID!
        title:String!
        subtitle:String!
        body:String!
        published:Boolean!
        author: String!
        upvotes: Int!
        downvotes: Int!
        commentCount: Int!
    }
    type Mutation{
        updatePost(
          id:ID!
          title:String!
          subtitle:String!
          body:String!
          published:Boolean!
          author: String!
          upvotes: Int!
          downvotes: Int!
          commentCount: Int!
        ): Post!
        deletePost(id: ID!): Post!
        createPost(
          id:ID!
          title:String!
          subtitle:String!
          body:String!
          published:Boolean!
          author: String!
          upvotes: Int!
          downvotes: Int!
          commentCount: Int!
        ): Post!
    }
    type Subscription {
        post: SubscriptionPayload!
    }
    
    type SubscriptionPayload {
        mutation: String!
        data: Post!
    }
`;
module.exports = typeDefs;
```

Other than the normal schema definitions for queries and mutation, we have a type called `Subscription` that is added on the post object via a custom type  `SubscriptionPayload`.

Therefore, each time a change is made to a post object, an event will be triggered for all who subscribe to events that return the name of the mutation performed  —  update, delete, and create and post data.

Now let’s code our `resolvers.js` for the above `typeDefs`.

```jsx
const posts = require('./postData');
//Resolvers - This are the set of the function defined to get the desired output for the given API
const resolvers = {
  Query:{
    
  },
  
  Mutation:{
   
  },
  
Subscription:{
    
  },
}
module.exports = resolvers;
```

## Coding objects

We first imported the `postData` and then added our `resolver` object, which contains our `query`, `mutation`, and `subscription` object.

Let’s code each object one by one .

### **Query object**

We will define two queries — `getPost` and `getPosts` — inside our `query` object.

```jsx
// return all posts
getPosts() {
  return posts;
},
// return post by args passed, for now it just check for body and 
// title for the post
getPost(parent, args){
  return posts.filter((post) => {
    const body =  post.body.toLowerCase().includes(args.query.toLowerCase())
    const title =  post.title.toLowerCase().includes(args.query.toLowerCase())
    return body || title;
  });
}
```

### Mutation object

We will define three mutations — `createPost` ,`updatePost`, and `deletePost` — inside our mutation object.

#### `createPost`

Check whether the post for the ID already exists. If yes, we’ll throw an error to GraphQL server. Otherwise, we’ll create the post from `args` and add it to our posts JSON data.

```jsx
createPost(parent, args, { pubsub }) {
  const id = parseInt(args.id, 10);
  const postIndex = posts.findIndex((post)=> post.id === id);
  if(postIndex === -1) {
    posts.push({
      ...args
    });
    
    pubsub.publish('post', {
      post:{
          mutation: 'CREATED',
          data: {...args}
      }
    }); 
    
   return {...args};
  };
  throw new Error('Post with same id already exist!');
}
```

We published an event called `CREATED` that will be triggered to all subscribers of the channel `post` through the socket and return newly created post data.

#### `updatePost`

We will check whether the post for the ID already exists. If it does, we’ll update the post with the `args` passed. Otherwise, it’ll throw an error.

```jsx
    updatePost(parent, args, { pubsub }){
      const id = parseInt(args.id, 10);
      const postIndex = posts.findIndex((post)=> post.id === id);
      if (postIndex !== -1) {
        const post = posts[postIndex];
        const updatedPost = {
          ...post,
          ...args
        };
      posts.splice(postIndex, 1, updatedPost);
      pubsub.publish('post', {
          post:{
              mutation: 'UPDATED',
              data: updatedPost
          }
        });
        return updatedPost;
      }
    throw new Error('Post does not exist!');
    }
```

As you can see, we again published a new event called `UPDATED` that returns the updated post data.

#### `deletePost`

We will check whether the post for the ID already exists. If it does, we’ll delete it from the posts array or throw an error.

```jsx
deletePost(parent, args, { pubsub }){
  const id = parseInt(args.id, 10);
  const isPostExists = posts.findIndex((post)=> post.id === id);
  if(isPostExists === -1) {
    throw new Error('Post does not exist!');
  }
  //splice will return the index of the removed items from the array object
  const [post] = posts.splice(isPostExists, 1);
  // return post;
pubsub.publish('post', {
    post:{
        mutation: 'DELETED',
        data: post
    }
  })
  return post;
},
```

Again, we published a new event called `DELETED` with the delete post data.

### Subscription object

This object uses a `pubsub.asyncIterator` function to map the event underlying the source stream to a returned response stream.

The `asyncIterator` takes the channel name through which the event across the app will be mapped out.

```jsx
post:{
  subscribe(parent, args, {pubsub}){
    return pubsub.asyncIterator('post');
  }
}
```

Now the only file left is the `index.js`. Add the following code to this file.

```jsx
const { GraphQLServer, PubSub } = require('graphql-yoga');
const typeDefs = require('./typeDefs');
const resolvers = require('./resolvers');
const pubsub = new PubSub()
const server  = new GraphQLServer({
  typeDefs,
  resolvers,
  context:{
    pubsub
  }
})
const options = {
  port: 3000
};
server.start(options, ({ port }) => {
  console.log(
    `Graphql Server started, listening on port ${port} for incoming requests.`,
  )
})
```

Here, we created a GraphQLServer, passed all our files, and started the server.

Finally, we’ll add a script to run our project in `package.json`.

```jsx
"scripts": {
  "start": "node index.js"
},
```

Open the terminal and run `npm start`. If everything is good, you’ll see the following message.

```jsx
Graphql Server started, listening on port 3000 for incoming requests.
```

Now head over to the browser and type `localhost:3000`. You’ll see a GraphQL Playground.

Just to check that everything is working as expected, let’s run a `getPosts` query.

![Running a getPosts Query in GraphQL](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2020/01/graphql-subscription-getpost-query.png?resize=730%2C299&ssl=1)

To start our subscription to the post changes, we’ll open up a new tab in GraphQL Playground and run the following.

```jsx
subscription{
  post{
    mutation
    data{
      id,
      title,
      subtitle,
      body,
      published
      author,
      upvotes,
      downvotes,
      commentCount,
    }
  }
}
```

This enables us to add a subscription to our channel post and start listening for any event published in the channel.

![Add GraphQL Subscriptions to Channel Post](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2020/01/add-graphql-subscription-to-channel-post.png?resize=730%2C390&ssl=1)

To see it in action, just perform any of the mutations. For example:

```jsx
mutation {
  updatePost(
    id: 8,
    downvotes:3,
    author: "deepak gupta",
    published: true,
    subtitle: "testinng subtitle",
    body: "testing body",
    commentCount: 12,
    upvotes: 4,
    title: "oh yeah :)"
  ) {
    id
  } 
}
```

As you can see, the post response stream gave back the data for the update event.

![Response Stream Data in a GraphQL Subscription](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2020/01/graphql-subscriptions-response-stream-data.png?resize=730%2C682&ssl=1)

## **Recapping the GraphQL subscription process**

To wrap up our tutorial, let’s quickly recap the subscription process. The subscription is defined below in `typeDefs.js`.

```jsx
type Subscription {
post: SubscriptionPayload!
}
type SubscriptionPayload {
mutation: String!
data: Post!
}
```

Use the pub/sub method provided by `graphql-yoga` to subscribe and publish. This can also facilitate mechanisms like `EventEmitter`.

```jsx
const { GraphQLServer, PubSub } = require('graphql-yoga');const pubsub = new PubSub()
const server = new GraphQLServer({
typeDefs,
resolvers,
context:{
pubsub
}
})
```

Implement the resolver for subscription type to map the event using `pubsub.asyncIterator`. Once we request a subscription from GraphQL Playground, it will add our socket to its listening socket list and send back events while we call `pubsub.publish`.

```jsx
post:{
subscribe(parent, args, {pubsub}){
return pubsub.asyncIterator('post');
}
}
```

Finally, call the `pubsub.publish()` method from the channel added mutation.

```jsx
pubsub.publish('post', {
post:{
mutation: 'UPDATED',
data: updatedPost
}
});
```

If you’ve followed these steps to a T, you’ve successfully created a GraphQL subscription, a real-time method to sync up client and server.

To see the above app in action, head over to [CodeSandbox](https://codesandbox.io/s/nodejs-graphql-server-onvms).

* * *
## 200's only ‎✅: Monitor failed and show GraphQL requests in production 

While GraphQL has some features for debugging requests and responses, making sure GraphQL reliably serves resources to your production app is where things get tougher. If you’re interested in ensuring network requests to the backend or third party services are successful, [try LogRocket.](https://www2.logrocket.com/signup-lr)

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/jd2amkhuiynfjf3i33qk.png)

[LogRocket](https://www2.logrocket.com/signup-lr) is like a DVR for web apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on problematic GraphQL requests to quickly understand the root cause. In addition, you can track Apollo client state and inspect GraphQL queries' key-value pairs.

LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. [Start monitoring for free.](https://www2.logrocket.com/signup-lr)
* * *

The post [GraphQL subscriptions with Node.js](https://blog.logrocket.com/graphql-subscriptions-with-node-js/) appeared first on [LogRocket Blog](https://blog.logrocket.com).