GraphQL Subscriptions with Node.js

GraphQL Subscriptions are a way to send real-time updates to clients when data changes.

This is a good solution for applications that need to publish events in real-time. It also provides an alternative for REST APIs for those who want to build their APIs in a more efficient way.

A GraphQL subscription is a GraphQL operation that can be used to subscribe to changes in data. It is similar to a query, but instead of returning a single result, it returns a stream of results.

In this article, we will look at how to implement GraphQL subscriptions with Node.js and Redis.

TL;DR

Link to the GitHub repository.

Our Sample GraphQL Subscriptions Architecture

What is GraphQL?

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.

What is a GraphQL Subscription?

A GraphQL subscription is a GraphQL operation that can be used to subscribe to changes in data. It is similar to a query, but instead of returning a single result, it returns a stream of results.

What is Redis?

Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes with radius queries and streams.

What is PubSub?

PubSub is a pattern that allows you to publish messages to a topic and subscribe to that topic. This is useful for decoupling applications and services from each other.

What is PubSub in GraphQL?

PubSub is a pattern that allows you to publish messages to a topic and subscribe to that topic. This is useful for decoupling applications and services from each other.

Prerequisites

  • Node.js
  • NestJS
  • Redis

Getting Started

*Note: This article assumes you have a basic understanding of GraphQL and TypeScript.

To get started, we will create a new NestJS project.

nest new graphql-subscriptions

Installing Dependencies

We will need to install the following dependencies:

  • @nestjs/graphql - GraphQL module for NestJS
  • graphql-subscriptions - GraphQL subscriptions
  • graphql-tools - GraphQL tools
  • ioredis - Redis client for Node.js
  • @nestjs/config - Configuration module for NestJS
npm i @nestjs/graphql graphql-subscriptions graphql-tools ioredis @nestjs/config

Setting up Redis

We will use Redis as our PubSub engine. To set up Redis, we will use the @nestjs/config module.

npm i @nestjs/config
// src/config/config.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),
  ],
})
export class ConfigModule {}
// src/config/config.service.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class ConfigService {
  constructor(private configService: ConfigService) {}

  getRedisHost(): string {
    return this.configService.get<string>('REDIS_HOST');
  }

  getRedisPort(): number {
    return this.configService.get<number>('REDIS_PORT');
  }
}
// src/config/config.ts
export default () => ({
  redis: {
    host: process.env.REDIS_HOST,
    port: process.env.REDIS_PORT,
  },
});
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ConfigService } from './config/config.service';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const configService = app.get(ConfigService);

  await app.listen(3000);
}
bootstrap();

Setting up GraphQL

We will use the @nestjs/graphql module to set up GraphQL.

// src/app.module.ts
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ConfigModule } from './config/config.module';
import { ConfigService } from './config/config.service';

@Module({
  imports: [
    ConfigModule,
    GraphQLModule.forRoot({
      autoSchemaFile: true,
      installSubscriptionHandlers: true,
      context: ({ req }) => ({ req }),
    }),
  ],
})
export class AppModule {}

Setting up PubSub

We will use the graphql-subscriptions package to set up PubSub.

// src/pubsub/pubsub.service.ts
import { Injectable } from '@nestjs/common';
import { PubSub } from 'graphql-subscriptions';
import { RedisPubSub } from 'graphql-redis-subscriptions';
import { ConfigService } from '../config/config.service';

@Injectable()
export class PubSubService {
  private pubSub: PubSub;

  constructor(private configService: ConfigService) {
    this.pubSub = new RedisPubSub({
      connection: {
        host: this.configService.getRedisHost(),
        port: this.configService.getRedisPort(),
      },
    });
  }

  publish(triggerName: string, payload: any) {
    this.pubSub.publish(triggerName, payload);
  }

  subscribe(triggerName: string) {
    return this.pubSub.asyncIterator(triggerName);
  }
}

Creating a Resolver

We will create a resolver that will publish an event every 5 seconds.

// src/pubsub/pubsub.resolver.ts
import { Resolver, Subscription } from '@nestjs/graphql';
import { PubSubService } from './pubsub.service';

@Resolver()
export class PubSubResolver {
  constructor(private pubSubService: PubSubService) {}

  @Subscription(() => String, {
    name: 'pubSub',
    resolve: (payload: any) => payload,
  })
  pubSub() {
    return {
      subscribe: () => this.pubSubService.subscribe('pubSub'),
    };
  }
}

Testing the Subscription

We can test the subscription using the GraphQL Playground.

npm run start:dev
subscription {
  pubSub
}
{
  "data": {
    "pubSub": "Hello World!"
  }
}

Conclusion

In this article, we learned how to set up GraphQL subscriptions using Redis as the PubSub engine. We also learned how to publish and subscribe to events using GraphQL subscriptions.

Resources

© 2022 AlterNayte page. All rights reserved.