GraphQL Subscriptions – Real-Time Data
Subscriptions let the server push data to the client automatically whenever something changes. Instead of the client repeatedly asking "did anything change?", the server sends an update the moment an event occurs. Chat apps, live dashboards, sports scoreboards, and stock tickers all use this pattern.
How Subscriptions Differ from Queries and Mutations
Query Mutation Subscription ───── ──────── ──────────── Client asks once Client triggers Client connects once Server responds once a change Server pushes updates Connection closes Connection closes Connection stays open HTTP request/response HTTP request/response WebSocket (persistent)
The WebSocket Connection
Subscriptions use a long-lived WebSocket connection instead of regular HTTP. WebSockets allow two-way communication — the server can push data at any time without the client asking first.
Regular HTTP (Query/Mutation): ────────────────────────────── Client ──── request ────► Server Client ◄─── response ─── Server Connection closes WebSocket (Subscription): ────────────────────────── Client ──── subscribe ───► Server (connect) Client ◄─── event data ── Server (push #1) Client ◄─── event data ── Server (push #2) Client ◄─── event data ── Server (push #3) Client ──── unsubscribe ─► Server (disconnect)
Defining a Subscription in the Schema
type Message {
id: ID!
text: String!
sender: String!
timestamp: String!
}
type Subscription {
messageSent(chatRoomId: ID!): Message!
}
type Mutation {
sendMessage(chatRoomId: ID!, text: String!): Message!
}
Writing a Subscription Operation
subscription ListenToRoom {
messageSent(chatRoomId: "room_42") {
id
text
sender
timestamp
}
}
Every time someone sends a message to room_42,
the server pushes this to all subscribed clients:
──────────────────────────────────────────────────
{
"data": {
"messageSent": {
"id": "msg_201",
"text": "Hello everyone!",
"sender": "Kavya",
"timestamp": "2025-06-01T10:35:00Z"
}
}
}
How the Server Handles Subscriptions
Client A subscribes
Client B subscribes ← Multiple subscribers to same event
Client C subscribes
Client D fires a mutation
(sendMessage)
Server detects event
│
▼
┌─────────────────────────┐
│ PubSub system │
│ (Redis / in-memory) │
│ Broadcasts to all │
│ active subscribers │
└───┬───────┬───────┬─────┘
│ │ │
▼ ▼ ▼
Client A Client B Client C
receives receives receives
the push the push the push
Setting Up Subscriptions with Apollo Server
npm install graphql-ws ws @graphql-tools/schema
// Server setup (simplified):
import { WebSocketServer } from 'ws';
import { useServer } from 'graphql-ws/lib/use/ws';
import { makeExecutableSchema } from '@graphql-tools/schema';
import { PubSub } from 'graphql-subscriptions';
const pubsub = new PubSub();
const resolvers = {
Mutation: {
sendMessage: (_, { chatRoomId, text, sender }) => {
const message = { id: Date.now(), text, sender };
pubsub.publish('MESSAGE_SENT_' + chatRoomId, {
messageSent: message
});
return message;
}
},
Subscription: {
messageSent: {
subscribe: (_, { chatRoomId }) =>
pubsub.asyncIterator('MESSAGE_SENT_' + chatRoomId)
}
}
};
When to Use Subscriptions
Good fit for subscriptions: Better with polling: ──────────────────────────── ──────────────────── Chat messages Dashboard refreshed every 5 min Live auction bids Occasional status checks Collaborative editing Infrequent data changes Real-time notifications Clients behind firewalls Sports scores blocking WebSockets
Key Points
- Subscriptions push data from server to client over a persistent WebSocket connection.
- The server publishes events through a PubSub system; all subscribed clients receive the update.
- Use subscriptions for high-frequency, real-time data where polling would be too slow or wasteful.
- A subscription stays open until the client unsubscribes or disconnects.
