Real-time applications are increasingly popular, providing instant data updates and creating interactive experiences for users. Express.js can support real-time updates through GraphQL subscriptions, allowing clients to receive live data changes from the server automatically.
GraphQL subscriptions allow clients to listen to specific events and receive real-time updates from the server. Unlike queries and mutations, which follow a request-response pattern, subscriptions use WebSockets for a continuous two-way communication channel.
WebSockets are a protocol enabling interactive communication sessions between a client and a server. Unlike HTTP, which requires constant requests to maintain an open channel, WebSockets allow both parties to send messages at any time, making them ideal for real-time applications.
Express.js, combined with libraries like graphql-ws
or subscriptions-transport-ws
, enables WebSocket support, bridging the gap between traditional request-response cycles and real-time data flow.
To use GraphQL subscriptions in Express, you’ll set up a WebSocket server alongside your HTTP server, configure the schema to include subscriptions, and use a pub-sub mechanism to emit events.
Install the following packages:
apollo-server-express
for handling GraphQL requests.graphql-subscriptions
for handling pub-sub events.graphql-ws
for WebSocket support in GraphQL subscriptions.
npm install apollo-server-express graphql graphql-subscriptions graphql-ws
Here’s an example of setting up a basic server with WebSocket support:
const express = require('express');
const { ApolloServer } = require('apollo-server-express');
const { makeExecutableSchema } = require('@graphql-tools/schema');
const { useServer } = require('graphql-ws/lib/use/ws');
const { WebSocketServer } = require('ws');
const { PubSub } = require('graphql-subscriptions');
// Create a pub-sub instance
const pubsub = new PubSub();
const MESSAGE_ADDED = 'MESSAGE_ADDED';
const typeDefs = `
type Message {
id: ID!
content: String!
}
type Query {
messages: [Message!]
}
type Subscription {
messageAdded: Message!
}
type Mutation {
addMessage(content: String!): Message!
}
`;
const messages = []; // In-memory storage for simplicity
const resolvers = {
Query: {
messages: () => messages,
},
Mutation: {
addMessage: (_, { content }) => {
const message = { id: messages.length + 1, content };
messages.push(message);
pubsub.publish(MESSAGE_ADDED, { messageAdded: message });
return message;
},
},
Subscription: {
messageAdded: {
subscribe: () => pubsub.asyncIterator(MESSAGE_ADDED),
},
},
};
// Create schema
const schema = makeExecutableSchema({ typeDefs, resolvers });
async function startServer() {
const app = express();
const httpServer = app.listen(4000, () => {
console.log(`Server is running at http://localhost:4000/graphql`);
});
// Set up WebSocket server
const wsServer = new WebSocketServer({
server: httpServer,
path: '/graphql',
});
useServer({ schema }, wsServer);
const server = new ApolloServer({ schema });
await server.start();
server.applyMiddleware({ app });
}
startServer();
In this setup:
Subscription
type defines a messageAdded
subscription.pubsub
object, an instance of PubSub
, is used to publish messages.useServer
is used to configure the WebSocket server with graphql-ws
.In the example above, we define a subscription called messageAdded
that notifies clients of new messages.
addMessage
takes content
as input, creates a new message, and publishes it to all subscribers.messageAdded
listens for MESSAGE_ADDED
events. Each time a new message is added, clients receive an update.To receive real-time updates on the client, you’ll use a GraphQL client like Apollo Client or any WebSocket GraphQL client to subscribe.
Here’s how a client might subscribe to messageAdded
subscription {
messageAdded {
id
content
}
}
Each time addMessage
is called, the client subscribed to messageAdded
will receive an instant update with the new message.
The PubSub
system in GraphQL Subscriptions allows events to be triggered and data to be broadcasted. When a user adds a message, the event MESSAGE_ADDED
is triggered, causing any clients subscribed to messageAdded
to be notified.
addMessage
.pubsub.publish(MESSAGE_ADDED, { messageAdded: message })
is called.messageAdded
subscription pushes the new message to all subscribed clients.This flow creates a seamless, real-time experience where users see messages appear instantly.
To limit the data each client receives, you can add arguments to the subscription:
subscription($userId: ID!) {
messageAdded(userId: $userId) {
id
content
}
}
const resolvers = {
Subscription: {
messageAdded: {
subscribe: (_, { userId }) =>
pubsub.asyncIterator(MESSAGE_ADDED).filter(
(payload) => payload.messageAdded.userId === userId
),
},
},
};
When working with subscriptions, it’s important to keep security and performance in mind:
wss
(WebSocket Secure) for encrypted connections.GraphQL subscriptions allow you to implement real-time features in Express.js applications, enhancing user interactivity and data immediacy. By combining Express.js with GraphQL and WebSockets, you can set up efficient, scalable systems that broadcast updates instantly to subscribed clients. Happy Coding!❤️