How GraphQL Subscriptions Work: Real-Time Data Updates Explained
GraphQL
subscriptions allow clients to receive real-time updates by opening a persistent connection to the server, usually via WebSocket. When data changes, the server pushes updates to subscribed clients automatically without the need for repeated queries.Syntax
A GraphQL subscription uses the subscription keyword similar to query or mutation. It defines the event or data changes the client wants to listen to.
subscription: Starts the subscription operation.- Event or field name: Specifies what data changes to listen for.
- Selection set: Defines the data fields to receive when an event occurs.
graphql
subscription OnMessageAdded {
messageAdded {
id
content
author
}
}Example
This example shows a simple subscription that listens for new messages added to a chat. When a new message is added, the server sends the id, content, and author fields to the client in real-time.
javascript
const { ApolloServer, gql, PubSub } = require('apollo-server'); const pubsub = new PubSub(); const MESSAGE_ADDED = 'MESSAGE_ADDED'; const typeDefs = gql` type Message { id: ID! content: String! author: String! } type Query { messages: [Message!] } type Subscription { messageAdded: Message } `; const messages = []; const resolvers = { Query: { messages: () => messages, }, Subscription: { messageAdded: { subscribe: () => pubsub.asyncIterator([MESSAGE_ADDED]), }, }, }; const server = new ApolloServer({ typeDefs, resolvers }); server.listen().then(({ url }) => { console.log(`Server ready at ${url}`); // Simulate new messages every 3 seconds let id = 1; setInterval(() => { const newMessage = { id: id++, content: 'Hello ' + id, author: 'User' + id }; messages.push(newMessage); pubsub.publish(MESSAGE_ADDED, { messageAdded: newMessage }); }, 3000); });
Output
Server ready at http://localhost:4000/
// Every 3 seconds, a new message is published and pushed to subscribed clients.
Common Pitfalls
Common mistakes when using GraphQL subscriptions include:
- Not using a persistent connection like WebSocket, which is required for real-time updates.
- Forgetting to implement the
subscriberesolver that returns an async iterator. - Trying to use subscriptions like queries or mutations without handling the continuous data stream.
- Not managing connection lifecycle events, leading to memory leaks or dropped subscriptions.
javascript
/* Wrong: Using a query resolver for subscription */ Subscription: { messageAdded: () => { // This returns a single value, not a stream return { id: 1, content: 'Hello', author: 'User1' }; }, } /* Right: Using asyncIterator for streaming data */ Subscription: { messageAdded: { subscribe: () => pubsub.asyncIterator(["MESSAGE_ADDED"]), }, }
Quick Reference
| Concept | Description |
|---|---|
| subscription | Keyword to define a subscription operation |
| subscribe resolver | Returns an async iterator to push data streams |
| PubSub | Utility to publish and subscribe to events |
| WebSocket | Common protocol to keep connection open for updates |
| Selection set | Fields requested in subscription response |
Key Takeaways
GraphQL subscriptions use persistent connections to push real-time data updates to clients.
The subscription resolver must return an async iterator to stream events.
WebSocket is the common transport protocol for subscriptions.
Subscriptions differ from queries by continuously sending data instead of a single response.
Proper connection and resource management is essential to avoid leaks and dropped updates.