GraphQL Subscriptions with Redis
By 2018, GraphQL has conquered the API world. But until recently, we still relied on traditional WebSockets or polling for real-time data. GraphQL Subscriptions have finally standardized how we push data from the server to the client.
The Subscription Schema
In your schema, you define subscriptions just like queries:
type Subscription {
messageAdded(roomId: ID!): Message
}
The Backend: Apollo Server and Redis
For a single server, the default PubSub implementation in memory works fine. But in production, you're likely running multiple instances of your server. This is where Redis PubSub becomes essential.
const { RedisPubSub } = require('graphql-redis-subscriptions');
const pubsub = new RedisPubSub({
connection: {
host: REDIS_DOMAIN_NAME,
port: PORT_NUMBER
}
});
const resolvers = {
Subscription: {
messageAdded: {
subscribe: (parent, { roomId }) => {
return pubsub.asyncIterator(`MESSAGE_ADDED_${roomId}`);
},
},
},
Mutation: {
addMessage: async (parent, { roomId, text }) => {
const newMessage = await saveMessage(roomId, text);
pubsub.publish(`MESSAGE_ADDED_${roomId}`, { messageAdded: newMessage });
return newMessage;
}
}
};
The Client: Subscription Transport WS
On the client, Apollo uses the subscriptions-transport-ws library to maintain a persistent connection.
const client = new ApolloClient({
link: split(
({ query }) => {
const definition = getMainDefinition(query);
return definition.kind === 'OperationDefinition' && definition.operation === 'subscription';
},
wsLink,
httpLink
),
cache: new InMemoryCache()
});
With this setup, our frontend can react to database changes in milliseconds without ever having to refresh. It's the "holy grail" of data synchronization in 2018.