SyntaxStudy
Sign Up
GraphQL Production Subscriptions with Redis PubSub
GraphQL Beginner 1 min read

Production Subscriptions with Redis PubSub

The in-memory PubSub that ships with graphql-subscriptions only works with a single server process. In a horizontally scaled deployment, a mutation handled by server A would not deliver events to subscribers connected to server B. Redis PubSub solves this by using Redis as a message broker. Every server publishes events to Redis and subscribes to the same channel. When an event arrives via Redis, the local graphql-subscriptions layer fans it out to the WebSocket connections managed by that process.
Example
// npm install graphql-redis-subscriptions ioredis

import { RedisPubSub } from 'graphql-redis-subscriptions';
import Redis from 'ioredis';

const options = {
  host: process.env.REDIS_HOST || '127.0.0.1',
  port: Number(process.env.REDIS_PORT) || 6379,
  password: process.env.REDIS_PASSWORD,
  retryStrategy: (times) => Math.min(times * 50, 2000),
};

// Two Redis connections: one pub, one sub
const pubsub = new RedisPubSub({
  publisher:  new Redis(options),
  subscriber: new Redis(options),
});

// Use exactly the same API as the in-memory PubSub
const EVENTS = {
  ORDER_UPDATED: 'ORDER_UPDATED',
  STOCK_CHANGED: 'STOCK_CHANGED',
};

const resolvers = {
  Mutation: {
    updateOrder: async (_, { id, status }) => {
      const order = await Order.update(id, { status });
      await pubsub.publish(EVENTS.ORDER_UPDATED, { orderUpdated: order });
      return order;
    },
  },
  Subscription: {
    orderUpdated: {
      subscribe: (_, { orderId }) =>
        withFilter(
          () => pubsub.asyncIterator(EVENTS.ORDER_UPDATED),
          (payload) => payload.orderUpdated.id === orderId
        )(),
    },
  },
};