GraphQL Subscriptions enable real-time communication between a client and a server using WebSockets. They allow a client to subscribe to specific events and get notified in real time when the server emits updates. This chapter will cover the implementation of GraphQL Subscriptions with Express.js from basics to advanced concepts.
GraphQL Subscriptions extend the capabilities of GraphQL APIs by supporting real-time updates. They are often used in scenarios like:
We’ll build a real-time chat application to demonstrate subscriptions.
Create a new project and install the required dependencies:
mkdir graphql-subscriptions-example
cd graphql-subscriptions-example
npm init -y
npm install express express-graphql graphql graphql-subscriptions ws
graphql-subscriptions-example/
├── index.js
├── schema.js
└── package.json
Define the GraphQL schema with a Subscription
type for real-time updates and a Mutation
type for adding data.
const { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLList } = require("graphql");
const { PubSub } = require("graphql-subscriptions");
const pubsub = new PubSub(); // Initialize PubSub
// In-memory storage for messages
const messages = [];
// Define Message Type
const MessageType = new GraphQLObjectType({
name: "Message",
fields: {
content: { type: GraphQLString },
sender: { type: GraphQLString },
},
});
// Define Query Type
const QueryType = new GraphQLObjectType({
name: "Query",
fields: {
messages: {
type: new GraphQLList(MessageType),
resolve: () => messages,
},
},
});
// Define Mutation Type
const MutationType = new GraphQLObjectType({
name: "Mutation",
fields: {
sendMessage: {
type: MessageType,
args: {
content: { type: GraphQLString },
sender: { type: GraphQLString },
},
resolve: (_, { content, sender }) => {
const newMessage = { content, sender };
messages.push(newMessage);
// Publish the event to subscribers
pubsub.publish("NEW_MESSAGE", { messageReceived: newMessage });
return newMessage;
},
},
},
});
// Define Subscription Type
const SubscriptionType = new GraphQLObjectType({
name: "Subscription",
fields: {
messageReceived: {
type: MessageType,
subscribe: () => pubsub.asyncIterator("NEW_MESSAGE"), // Subscribe to the "NEW_MESSAGE" topic
},
},
});
// Define Schema
const schema = new GraphQLSchema({
query: QueryType,
mutation: MutationType,
subscription: SubscriptionType,
});
module.exports = schema;
Configure the Express server to handle GraphQL requests and enable WebSocket support for subscriptions.
const express = require("express");
const { graphqlHTTP } = require("express-graphql");
const { execute, subscribe } = require("graphql");
const { createServer } = require("http");
const { WebSocketServer } = require("ws");
const { useServer } = require("graphql-ws/lib/use/ws");
const schema = require("./schema");
const app = express();
// GraphQL Endpoint
app.use(
"/graphql",
graphqlHTTP({
schema,
graphiql: {
subscriptionEndpoint: "ws://localhost:4000/subscriptions", // WebSocket endpoint
},
})
);
// Create an HTTP server
const server = createServer(app);
// Create WebSocket server for GraphQL Subscriptions
const wsServer = new WebSocketServer({
server,
path: "/subscriptions",
});
// Use GraphQL Subscriptions with WebSocket
useServer({ schema, execute, subscribe }, wsServer);
// Start the server
server.listen(4000, () => {
console.log("Server is running on http://localhost:4000/graphql");
console.log("Subscriptions are running on ws://localhost:4000/subscriptions");
});
You can test the application using GraphiQL (GraphQL IDE) or a GraphQL client like Apollo.
Fetch all messages:
query {
messages {
content
sender
}
}
Send a new message:
mutation {
sendMessage(content: "Hello, world!", sender: "Alice") {
content
sender
}
}
Listen for new messages:
subscription {
messageReceived {
content
sender
}
}
Whenever you execute the sendMessage
mutation, clients subscribed to messageReceived
will receive real-time updates.
To secure subscriptions, add middleware to validate WebSocket connections.
useServer({
schema,
execute,
subscribe,
onConnect: (ctx) => {
const token = ctx.connectionParams?.authorization;
if (!validateToken(token)) {
throw new Error("Unauthorized");
}
},
}, wsServer);
For distributed systems, use Redis to share events across multiple instances of your app.
Install Redis and the required library:
npm install ioredis graphql-redis-subscriptions
Update PubSub initialization
const { RedisPubSub } = require("graphql-redis-subscriptions");
const pubsub = new RedisPubSub({
connection: {
host: "localhost",
port: 6379,
},
});
In this chapter, you learned how to implement GraphQL Subscriptions with Express.js: Configured a real-time subscription server using WebSockets. Built a chat application to demonstrate the Subscription type. Secured the implementation with advanced features like authentication and scaling. This implementation showcases how Express.js can handle real-time updates efficiently using GraphQL Subscriptions, making it a powerful choice for modern web applications. Happy coding !❤️