GraphQL subscriptions enable real-time communication between your server and clients. While queries fetch data once and mutations modify it, subscriptions create a continuous connection that pushes updates to clients as they happen.
This guide covers everything from basic subscription setup to production-ready implementations with WebSockets. Use our GraphQL formatter to beautify your subscription queries and schema validator to check your schemas.
What Are GraphQL Subscriptions?
Subscriptions are the third operation type in GraphQL (alongside queries and mutations). They allow clients to:
- •Receive real-time updates when data changes on the server
- •Maintain a persistent connection via WebSockets
- •Get notified of events like new messages, updates, or deletions
- •Build reactive UIs that update automatically without polling
Query
Request-Response
One-time fetch
Mutation
Request-Response
Modify data once
Subscription
Persistent Connection
Continuous updates
How Subscriptions Work
The Flow
Transport Protocol: WebSockets
Subscriptions typically use WebSockets for bi-directional, full-duplex communication:
// Traditional HTTP Client → Server: GET /api/messages Server → Client: [messages] // Connection closes // WebSocket for Subscriptions Client ↔ Server: Persistent connection established Server → Client: New message arrives (pushed automatically) Server → Client: Another message arrives Server → Client: Message updated // Connection stays open until closed
Your First Subscription
Schema Definition
type Subscription {
messageAdded(chatId: ID!): Message!
}
type Message {
id: ID!
text: String!
author: User!
createdAt: String!
}
type User {
id: ID!
name: String!
avatar: String
}Client Subscription Query
subscription OnMessageAdded($chatId: ID!) {
messageAdded(chatId: $chatId) {
id
text
author {
name
avatar
}
createdAt
}
}
# Variables
{
"chatId": "chat-123"
}This subscription will receive updates whenever a message is added to chat-123
Server Implementation
Apollo Server Example
const { ApolloServer, gql, PubSub } = require('apollo-server');
const pubsub = new PubSub();
const MESSAGE_ADDED = 'MESSAGE_ADDED';
// Schema
const typeDefs = gql`
type Subscription {
messageAdded(chatId: ID!): Message!
}
type Message {
id: ID!
text: String!
chatId: ID!
author: User!
createdAt: String!
}
type Mutation {
sendMessage(chatId: ID!, text: String!): Message!
}
`;
// Resolvers
const resolvers = {
Mutation: {
sendMessage: (parent, { chatId, text }, context) => {
const message = {
id: String(Date.now()),
text,
chatId,
author: context.user,
createdAt: new Date().toISOString()
};
// Publish the message to subscribers
pubsub.publish(MESSAGE_ADDED, {
messageAdded: message
});
return message;
}
},
Subscription: {
messageAdded: {
subscribe: (parent, { chatId }) => {
// Return an async iterator that filters by chatId
return pubsub.asyncIterator([MESSAGE_ADDED]);
},
resolve: (payload, { chatId }) => {
// Only send if message is for this chat
if (payload.messageAdded.chatId === chatId) {
return payload.messageAdded;
}
return null;
}
}
}
};
const server = new ApolloServer({
typeDefs,
resolvers,
subscriptions: {
path: '/subscriptions',
}
});
server.listen().then(({ url, subscriptionsUrl }) => {
console.log(`Server ready at ${url}`);
console.log(`Subscriptions ready at ${subscriptionsUrl}`);
});Key Point: The PubSub pattern decouples event publishers (mutations) from subscribers. When data changes, publish an event, and all active subscriptions receive it.
Client Implementation
Apollo Client Setup
import { ApolloClient, InMemoryCache, split, HttpLink } from '@apollo/client';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { getMainDefinition } from '@apollo/client/utilities';
import { createClient } from 'graphql-ws';
// HTTP connection for queries and mutations
const httpLink = new HttpLink({
uri: 'http://localhost:4000/graphql'
});
// WebSocket connection for subscriptions
const wsLink = new GraphQLWsLink(createClient({
url: 'ws://localhost:4000/subscriptions'
}));
// Split traffic based on operation type
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink, // Use WebSocket for subscriptions
httpLink, // Use HTTP for queries and mutations
);
const client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache()
});React Component Example
import { useSubscription, gql } from '@apollo/client';
const MESSAGE_SUBSCRIPTION = gql`
subscription OnMessageAdded($chatId: ID!) {
messageAdded(chatId: $chatId) {
id
text
author {
name
avatar
}
createdAt
}
}
`;
function ChatRoom({ chatId }) {
const [messages, setMessages] = useState([]);
const { data, loading, error } = useSubscription(
MESSAGE_SUBSCRIPTION,
{ variables: { chatId } }
);
useEffect(() => {
if (data) {
// Add new message to the list
setMessages(prev => [...prev, data.messageAdded]);
}
}, [data]);
if (loading) return <p>Connecting...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
{messages.map(message => (
<div key={message.id}>
<strong>{message.author.name}:</strong> {message.text}
</div>
))}
</div>
);
}Common Subscription Use Cases
1. Chat Applications
Real-time messaging with typing indicators and read receipts:
type Subscription {
messageAdded(chatId: ID!): Message!
userTyping(chatId: ID!): User!
messageRead(chatId: ID!): ReadReceipt!
}2. Live Dashboards
Analytics, metrics, and monitoring updates:
type Subscription {
metricsUpdated: Metrics!
alertTriggered: Alert!
systemStatus: Status!
}3. Collaborative Editing
Google Docs-style real-time collaboration:
type Subscription {
documentUpdated(docId: ID!): Document!
cursorMoved(docId: ID!): CursorPosition!
userJoined(docId: ID!): User!
userLeft(docId: ID!): User!
}4. Social Media Feeds
New posts, likes, and comments in real-time:
type Subscription {
postAdded: Post!
postLiked(postId: ID!): Like!
commentAdded(postId: ID!): Comment!
}5. Gaming & Multiplayer
Game state, player moves, and events:
type Subscription {
gameStateChanged(gameId: ID!): GameState!
playerMoved(gameId: ID!): PlayerPosition!
gameEvent(gameId: ID!): GameEvent!
}6. E-commerce & Orders
Order status, inventory, and pricing updates:
type Subscription {
orderStatusChanged(userId: ID!): Order!
productPriceChanged(productId: ID!): Product!
stockUpdated(productId: ID!): Stock!
}Subscription Best Practices
✅ Use Filters
Filter events server-side to reduce unnecessary data sent to clients.
✅ Implement Authentication
Verify user permissions before establishing subscription connections.
✅ Handle Reconnections
Implement reconnection logic for when WebSocket connections drop.
✅ Rate Limit
Protect servers from subscription abuse with rate limiting.
✅ Use Redis PubSub
For scaled deployments, use Redis instead of in-memory PubSub.
✅ Monitor Connections
Track active subscriptions and connection health.
❌ Don't Over-Subscribe
Too many subscriptions can overwhelm clients and servers.
❌ Avoid Heavy Payloads
Keep subscription data lightweight for better performance.
Scaling Subscriptions in Production
Redis PubSub for Multi-Server Setup
When running multiple server instances, use Redis to share subscription events:
const { RedisPubSub } = require('graphql-redis-subscriptions');
const Redis = require('ioredis');
const options = {
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT,
retryStrategy: times => Math.min(times * 50, 2000)
};
const pubsub = new RedisPubSub({
publisher: new Redis(options),
subscriber: new Redis(options)
});
// Now all server instances share the same PubSubConnection Limits
Set reasonable limits to protect your infrastructure:
- • Max connections per user: 5-10
- • Max subscriptions per connection: 10-20
- • Idle connection timeout: 5-10 minutes
- • Heartbeat/ping interval: 30 seconds
GraphQL Development Tools
GraphQL Formatter
Format subscription queries
Schema Validator
Validate schema syntax
GraphQL to TypeScript
Generate TypeScript types
JSON to GraphQL
Generate schemas
GraphQL Minifier
Compress queries
GraphQL to JSON
Convert to JSON
GraphQL to JSON Schema
Generate JSON Schema
JSON Formatter
Format JSON data
JSON Validator
Validate JSON
Related GraphQL Guides
External Resources
Official Documentation
- •GraphQL.org Subscriptions - Official subscription guide
- •Apollo Subscriptions - Client-side implementation
- •WebSocket Transport - WebSocket implementation
Libraries & Tools
- •GraphQL Redis Subscriptions - Redis PubSub for GraphQL
- •graphql-ws - Modern WebSocket protocol