GraphQL Subscriptions allow clients to receive real-time updates from the server. Unlike typical queries and mutations, subscriptions maintain an open connection and push data to clients whenever there is a change. This is useful for applications that require real-time updates like messaging apps, live dashboards, or collaborative tools.This chapter will explore GraphQL subscriptions in Node.js, from basic concepts to advanced implementation, with deep dives into real-world examples.
A GraphQL Subscription is a way for a server to send real-time updates to the client through an open WebSocket connection. The client subscribes to specific events or data changes, and the server pushes updates as they happen.
To implement GraphQL Subscriptions in Node.js, we’ll use:
npm install express apollo-server-express graphql graphql-ws
We’ll create a simple chat application where users can subscribe to new messages in real-time.
Define the schema: It will include Message
type, a Query
for fetching messages, a Mutation
for sending new messages, and a Subscription
for listening to new messages.
Implement resolvers: These will handle the queries, mutations, and the subscription.
const express = require('express');
const { ApolloServer, gql, PubSub } = require('apollo-server-express');
const { useServer } = require('graphql-ws/lib/use/ws');
const { WebSocketServer } = require('ws');
// Initialize PubSub for handling subscriptions
const pubsub = new PubSub();
const MESSAGE_ADDED = 'MESSAGE_ADDED';
// Sample data
let messages = [
{ id: '1', content: 'Hello World!', user: 'John' },
];
// Define the GraphQL schema
const typeDefs = gql`
type Message {
id: ID!
content: String!
user: String!
}
type Query {
messages: [Message]
}
type Mutation {
addMessage(content: String!, user: String!): Message
}
type Subscription {
messageAdded: Message
}
`;
// Define resolvers
const resolvers = {
Query: {
messages: () => messages,
},
Mutation: {
addMessage: (parent, { content, user }) => {
const newMessage = { id: String(messages.length + 1), content, user };
messages.push(newMessage);
// Publish the new message event
pubsub.publish(MESSAGE_ADDED, { messageAdded: newMessage });
return newMessage;
},
},
Subscription: {
messageAdded: {
subscribe: () => pubsub.asyncIterator([MESSAGE_ADDED]),
},
},
};
// Create an Express app and WebSocket server
const app = express();
const server = new ApolloServer({ typeDefs, resolvers });
const wsServer = new WebSocketServer({ noServer: true });
const PORT = 4000;
// Apply WebSocket server middleware
const httpServer = app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
// Integrate WebSocket with GraphQL Subscriptions
useServer({ schema: server.schema }, wsServer);
// Handle WebSocket connections
httpServer.on('upgrade', (request, socket, head) => {
wsServer.handleUpgrade(request, socket, head, (ws) => {
wsServer.emit('connection', ws, request);
});
});
server.start().then(() => {
server.applyMiddleware({ app });
console.log(`Subscriptions ready at ws://localhost:${PORT}/graphql`);
});
messageAdded
in real-time.messageAdded
subscription allows clients to listen for new messages.graphql-ws
library handles the WebSocket protocol, while the PubSub
mechanism publishes the message events to all subscribed clients.Once your server is running, clients can use queries and subscriptions.
To subscribe to new messages, the client sends the following subscription query
node -v
npm -v
subscription {
messageAdded {
id
content
user
}
}
This keeps the connection open, and whenever a new message is added, the client will automatically receive the update.
To add a new message, the client uses a mutation:
mutation {
addMessage(content: "Hello!", user: "Alice") {
id
content
user
}
}
Whenever this mutation is called, the message is added to the chat, and all clients subscribed to the messageAdded
event will be notified in real-time.
You may want to allow clients to subscribe to specific events or data changes based on certain conditions. This can be done by filtering subscriptions on the server.
Subscription: {
messageAdded: {
subscribe: (parent, { user }) => {
return pubsub.asyncIterator(MESSAGE_ADDED).filter(message => message.user === user);
},
},
}
In this case, clients would subscribe to new messages for a specific user.
subscription {
messageAdded(user: "John") {
id
content
user
}
}
While PubSub
works well for small applications, it’s not ideal for large-scale deployments because it only works in-memory. To scale, use systems like Redis, Kafka, or RabbitMQ to handle subscriptions across multiple instances of your app.
To use Redis for subscription events:
npm install graphql-redis-subscriptions ioredis
Then replace the PubSub
with Redis:
const { RedisPubSub } = require('graphql-redis-subscriptions');
const Redis = require('ioredis');
const pubsub = new RedisPubSub({
publisher: new Redis(),
subscriber: new Redis(),
});
This setup allows your subscriptions to scale across multiple instances of your app.
You may need to authenticate users before allowing them to subscribe. This can be done by verifying tokens inside the WebSocket connection.
const { useServer } = require('graphql-ws/lib/use/ws');
useServer({
onConnect: (ctx) => {
const token = ctx.connectionParams.authorization;
const user = jwt.verify(token, 'secret-key');
return { user };
},
}, wsServer);
In this example, the client must pass an authorization token with the WebSocket connection, and the token is verified before the subscription is allowed.
GraphQL Subscriptions can power live chat systems. As new messages are sent, all users receive updates instantly.
You can use subscriptions to build live dashboards that display real-time metrics, stock prices, or IoT sensor data.
In applications like Google Docs, GraphQL subscriptions can be used to push changes to all users working on the same document in real time.
Subscriptions are great for pushing real-time notifications, such as alerts for new messages, social media interactions, or important system events.
GraphQL Subscriptions bring real-time functionality to your API by maintaining an open WebSocket connection and allowing clients to receive updates as they happen. In this chapter, we've covered how to set up subscriptions in a Node.js environment, the role of PubSub, handling WebSockets, and some advanced topics like filters, scaling, and authentication.Subscriptions are perfect for applications that need to push real-time data to clients efficiently and are becoming increasingly popular for modern web and mobile apps. This chapter gives you a complete overview of building, scaling, and securing GraphQL Subscriptions, eliminating the need for any further resources on the subject. Happy coding !❤️