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.

Leave a Comment