Subscriptions
Understand real-time data delivery with GraphQL subscriptions.
What is a Subscription?
A subscription is a long-lived operation that delivers data to the client whenever a specific event occurs on the server. Unlike queries and mutations (request-response), subscriptions maintain an open connection.
┌─────────────────────────────────────────────────────────────────────┐
│ QUERY vs MUTATION vs SUBSCRIPTION │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ QUERY MUTATION SUBSCRIPTION │
│ ───────────────────────────────────────────────────────────────── │
│ Request → Response Request → Response Open connection │
│ One-time fetch One-time write Continuous delivery │
│ Client initiates Client initiates Server pushes │
│ Stateless Stateless Stateful │
│ │
│ "Give me data" "Change data" "Tell me when X happens" │
│ │
└─────────────────────────────────────────────────────────────────────┘
Why Subscriptions?
Traditional approaches to real-time data have limitations:
Polling
Client: "Any new messages?" (every 1 second)
Server: "No"
Client: "Any new messages?"
Server: "No"
Client: "Any new messages?"
Server: "Yes, here's one"
Problems: Wasted requests, latency (up to polling interval), server load
Long Polling
Client holds connection open until data is available, then reconnects. Problems: Complex, connection overhead, not true real-time
Subscriptions
Client: "Tell me when there are new messages"
Server: (connection stays open)
... time passes ...
Server: "New message: Hello!"
Server: "New message: How are you?"
Benefits: Instant delivery, efficient, single connection
Subscription Syntax
Subscriptions look like queries but use the subscription keyword:
subscription OnMovieAdded {
movieAdded {
id
title
releaseYear
genre
}
}
When a new movie is created, the server pushes:
{
"data": {
"movieAdded": {
"id": "99",
"title": "Dune: Part Two",
"releaseYear": 2024,
"genre": "SCIFI"
}
}
}
Subscriptions in the Schema
Define subscriptions in the Subscription type:
type Subscription {
"""
Triggered when a new movie is added to the database.
"""
movieAdded: Movie!
"""
Triggered when any movie is updated.
"""
movieUpdated: Movie!
"""
Triggered when a movie is deleted.
"""
movieDeleted: MovieDeletedPayload!
"""
Triggered when a new review is posted for a specific movie.
"""
reviewAdded(movieId: ID!): Review!
"""
Triggered when the Oscar ceremony announces an award.
"""
awardAnnounced(category: String): Award!
}
type MovieDeletedPayload {
id: ID!
title: String!
deletedAt: DateTime!
}
Subscription Arguments
Subscriptions can accept arguments to filter events:
# Subscribe to reviews for a specific movie
subscription OnReviewAdded($movieId: ID!) {
reviewAdded(movieId: $movieId) {
id
rating
comment
user {
displayName
}
}
}
Variables:
{
"movieId": "42"
}
Only reviews for movie 42 will be delivered. Reviews for other movies are filtered out.
Common Filtering Patterns
type Subscription {
# Filter by ID
movieUpdated(movieId: ID): Movie!
# Filter by type/category
awardAnnounced(category: String): Award!
# Filter by user
notificationReceived(userId: ID!): Notification!
# Multiple filters
messageReceived(channelId: ID!, priority: Priority): Message!
}
Transport Protocols
Unlike queries and mutations (HTTP), subscriptions require a persistent connection:
WebSocket (Most Common)
┌─────────────────────────────────────────────────────────────────────┐
│ WEBSOCKET FLOW │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ CLIENT SERVER │
│ │ │ │
│ │──── WebSocket Handshake ─────────────▶│ │
│ │◀─── Connection Established ──────────│ │
│ │ │ │
│ │──── Subscribe (movieAdded) ──────────▶│ │
│ │◀─── Acknowledgment ──────────────────│ │
│ │ │ │
│ │ (waiting for events...) │ │
│ │ │ │
│ │◀─── Data: New movie added ───────────│ │
│ │◀─── Data: Another movie added ───────│ │
│ │ │ │
│ │──── Unsubscribe ─────────────────────▶│ │
│ │◀─── Complete ────────────────────────│ │
│ │ │ │
└─────────────────────────────────────────────────────────────────────┘
Protocols built on WebSocket:
graphql-ws(modern, recommended)subscriptions-transport-ws(legacy, deprecated)
Server-Sent Events (SSE)
One-way communication from server to client. Simpler than WebSocket but less flexible. Works over HTTP.
HTTP Streaming
Some implementations use HTTP streaming for subscriptions. Useful when WebSocket isn't available.
Subscription Events
Subscriptions are triggered by events on the server. Common patterns:
Database Changes
User creates movie → Trigger "movieAdded" subscription
User updates movie → Trigger "movieUpdated" subscription
User deletes movie → Trigger "movieDeleted" subscription
External Events
Payment processed → Trigger "paymentReceived" subscription
File uploaded → Trigger "uploadCompleted" subscription
API webhook → Trigger relevant subscription
Scheduled Events
Every minute → Trigger "stockPriceUpdated" subscription
Every hour → Trigger "weatherUpdated" subscription
Use Cases
Real-Time Notifications
subscription {
notificationReceived {
id
type
message
createdAt
}
}
Live Chat
subscription OnMessage($channelId: ID!) {
messageReceived(channelId: $channelId) {
id
content
sender {
name
avatar
}
sentAt
}
}
Live Updates (Dashboards, Feeds)
subscription {
stockPriceUpdated(symbols: ["AAPL", "GOOGL"]) {
symbol
price
change
updatedAt
}
}
Collaborative Editing
subscription OnDocumentChanged($documentId: ID!) {
documentChanged(documentId: $documentId) {
changeType
path
value
editor {
name
}
}
}
Live Scores
subscription {
scoreUpdated(gameId: "superbowl-2024") {
homeTeam
homeScore
awayTeam
awayScore
quarter
timeRemaining
}
}
Subscription Lifecycle
┌─────────────────────────────────────────────────────────────────────┐
│ SUBSCRIPTION LIFECYCLE │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. SUBSCRIBE │
│ Client sends subscription query │
│ Server validates and registers subscription │
│ │
│ 2. LISTEN │
│ Server listens for relevant events │
│ Connection remains open │
│ │
│ 3. PUBLISH (repeats) │
│ Event occurs → Server filters subscriptions │
│ Matching subscriptions receive data │
│ │
│ 4. UNSUBSCRIBE │
│ Client closes connection or sends unsubscribe │
│ Server cleans up subscription │
│ │
│ Triggers for unsubscribe: │
│ - Client explicitly unsubscribes │
│ - Client disconnects (network, tab close) │
│ - Server terminates (error, shutdown) │
│ - Subscription completes (if finite) │
│ │
└─────────────────────────────────────────────────────────────────────┘
Design Considerations
1. Keep Payloads Small
# ✅ Good - minimal payload
subscription {
movieUpdated {
id
title
rating
}
}
# ❌ Avoid - huge payload on every update
subscription {
movieUpdated {
id
title
fullPlot
allReviews { ... }
allActors { ... }
allAwards { ... }
}
}
2. Filter on the Server
# ✅ Good - server filters
subscription {
reviewAdded(movieId: "42") { ... }
}
# ❌ Avoid - client filters from all events
subscription {
reviewAdded {
movieId # Client checks if movieId === "42"
...
}
}
3. Handle Reconnection
Clients should handle:
- Connection drops
- Server restarts
- Network changes
Most client libraries handle reconnection automatically.
4. Consider Scale
Subscriptions are stateful. At scale, consider:
- How many concurrent connections?
- How to distribute across servers? (pub/sub systems like Redis)
- Memory usage per subscription
When NOT to Use Subscriptions
Subscriptions aren't always the best choice:
| Scenario | Better Alternative |
|---|---|
| Data changes rarely (< 1/min) | Polling |
| User can tolerate delay | Polling with reasonable interval |
| Simple read-after-write | Return updated data from mutation |
| One-time data fetch | Query |
| High volume, low priority | Polling or batch updates |
Summary
| Concept | Description |
|---|---|
| Subscription | Long-lived operation for real-time data |
| Event | Server-side trigger that sends data to subscribers |
| WebSocket | Common transport for subscriptions |
| Filtering | Arguments to receive only relevant events |
| Lifecycle | Subscribe → Listen → Publish → Unsubscribe |
What's Next?
In the next chapter, we'll explore Introspection - how GraphQL APIs describe themselves and enable powerful tooling.