Setting Up Subscriptions for Real-Time Data with Apollo Server 4

Lesson Overview

Welcome to this lesson on "Setting Up Subscriptions for Real-Time Data". In this lesson, we will discuss real-time data and subscriptions to events in GraphQL.

GraphQL Subscriptions enable clients to listen for real-time updates from the server. When an event that matches the subscription’s criteria occurs, the server sends the updated data to the client automatically.

This is how subscriptions differ from Queries and Mutations:

  • Queries: Request data from the server.
  • Mutations: Modify data on the server.
  • Subscriptions: Receive updates whenever data is changed as specified.

Setting Up Apollo Server with Subscriptions

Let's begin by setting up our Apollo Server to handle subscriptions. We'll start by initializing our server and defining our schema, including the Subscription type.

We'll also be using graphql-ws for WebSocket communication, which allows our resolvers to notify clients about real-time updates.

import { ApolloServer } from '@apollo/server';
import { expressMiddleware } from '@apollo/server/express4';
import { makeExecutableSchema } from '@graphql-tools/schema';
import express from 'express';
import { PubSub } from 'graphql-subscriptions';
import { useServer } from 'graphql-ws/lib/use/ws';
import { createServer } from 'http';
import { WebSocketServer } from 'ws';
import { v4 as uuidv4 } from 'uuid';

// Initialize PubSub
const pubsub = new PubSub();

Defining Schema and Resolvers with Subscriptions

In this section, we'll define the schema with a type definition that includes a Subscription type.

const typeDefs = `#graphql
  type Book {
    id: ID!
    title: String!
    author: String!
  }

  type Query {
    books: [Book]
  }

  type Mutation {
    addBook(title: String!, author: String!): Book
  }

  type Subscription {
    bookAdded: Book
  }
`;

The Subscription type defines a bookAdded field, which is of type Book.

Writing Resolver Functions

Next, we need to implement resolver functions for these subscriptions. We will use Pubsub for it.

Pubsub serves as an event bus that allows publishing events (pubsub.publish) and subscribing to those events (pubsub.asyncIterator). In this case, it manages the event stream for the BOOK_ADDED event, enabling clients to subscribe to notifications about newly added books. Whenever the server publishes a BOOK_ADDED event, the asyncIterator sends the event's data (bookAdded: newBook) to all subscribed clients. Each event in this stream corresponds to a new book being added.

This allows subscribed clients to receive real-time updates about the new book.

// Sample data
let books = [
  { id: '1', title: 'The Hobbit', author: 'J.R.R. Tolkien' },
  { id: '2', title: 'Harry Potter', author: 'J.K. Rowling' },
];

const resolvers = {
  Query: {
    books: () => books,
  },
  Mutation: {
    addBook: (_: any, { title, author }: { title: string, author: string }) => {
      const newBook = { id: uuidv4(), title, author };
      books.push(newBook);
      pubsub.publish('BOOK_ADDED', { bookAdded: newBook });
      return newBook;
    },
  },
  Subscription: {
    bookAdded: {
      subscribe: () => pubsub.asyncIterator(['BOOK_ADDED']),
    },
  },
};

Here, when a book is added using the addBook mutation, the new book data is sent to all clients subscribing to the bookAdded subscription through the pubsub.publish method.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal