Skip to content
API Design Learn/GraphQL Real-Time & Security
Browsing as a guest. Sign in to save your progress and earn XP as you complete chapters.

Subscriptions & Real-Time Data

7 min read

You'll learn to

  • -Define a GraphQL subscription and explain how it differs structurally from a query or mutation
  • -Design event filtering so a subscriber only receives updates relevant to what they actually subscribed to

Queries and mutations are both request-response - the client asks, the server answers once. A subscription is GraphQL's third root operation type, for cases where the client wants to be pushed updates over time rather than asking repeatedly: new messages in a chat, live order status changes, a live-updating dashboard.

Defining a Subscription

A subscription root field, structurally similar to Query/Mutation
type Subscription {
  orderStatusChanged(orderId: ID!): Order!
}

subscription {
  orderStatusChanged(orderId: "501") {
    id
    status
    updatedAt
  }
}

A subscription is defined the same way as a query field, but instead of resolving once and returning, it stays open (typically over a WebSocket) and pushes a new payload to the client every time the underlying event fires - here, every time order 501's status actually changes, not on a polling interval.

Filtering: Only Send What This Subscriber Asked For

A subscription resolver filtering the event stream to just this subscriber's argument
def resolve_order_status_changed(root, info, order_id):
    # A shared pubsub channel receives events for EVERY order.
    # This subscriber only asked about order_id="501" - filter here,
    # not at the client, so irrelevant events never even get pushed.
    return pubsub.subscribe("order_status_changed", filter_fn=
        lambda event: event["order_id"] == order_id
    )

The naive mistake is pushing every event on a shared channel to every subscriber and letting the client filter client-side - that wastes bandwidth on data the client will just discard, and for a busy system (thousands of orders changing status per minute) it means every subscriber's connection carries irrelevant traffic. Filtering server-side, scoped to exactly the arguments this specific subscriber provided (`orderId: "501"`), means each client's connection only ever carries events it actually asked for.

Subscriptions Are Not a Replacement for Queries

A subscription typically pushes the delta or the changed state, not the full picture a client needs on first load - a well-designed real-time feature usually pairs an initial query (to fetch the current full state when the client first connects) with a subscription (to receive updates from that point forward), rather than expecting the subscription alone to somehow deliver both the initial state and every subsequent change.

Subscriptions hold an open connection per subscribed client, which is a meaningfully different resource cost than a stateless request-response query - at real scale, this needs its own connection-management and horizontal-scaling story (a pubsub backend fanning out to many server instances), not just "add a WebSocket handler" bolted onto the existing query/mutation infrastructure.

Interview Signal is part of Pro

See a real weak answer next to a real strong one for this exact topic.

Quiz is part of Pro

Test what you just read with a short quiz, and bank the XP.

Ready to Build This?

Design Real-Time Feed in the API Design Lab's GraphQL Mastery act.

ScaleDojo Logo
Initializing ScaleDojo