|
| 1 | +--- |
| 2 | +title: Subscriptions |
| 3 | +--- |
| 4 | + |
| 5 | +# Subscriptions |
| 6 | + |
| 7 | +Subscriptions allow a GraphQL server to push updates to clients in real time. Unlike queries and mutations, which use a request/response model, subscriptions maintain a long-lived connection between the client and server to stream data as events occur. This is useful for building features like chat apps, live dashboards, or collaborative tools that require real-time updates. |
| 8 | + |
| 9 | +This guide covers how to implement subscriptions in GraphQL.js, when to use them, and what to consider in production environments. |
| 10 | + |
| 11 | +## What is a subscription? |
| 12 | + |
| 13 | +A subscription is a GraphQL operation that delivers ongoing results to the client when a specific event happens. Unlike queries or mutations, which return a single response, a subscription delivers data over time through a persistent connection. |
| 14 | + |
| 15 | +GraphQL.js implements the subscription execution algorithm, but it's up to you to connect it to your event system and transport layer. Most implementations use WebSockets for transport, though any full-duplex protocol can work. |
| 16 | + |
| 17 | +## How execution works |
| 18 | + |
| 19 | +The core of subscription execution in GraphQL.js is the `subscribe` function. It works similarly to `graphql()`, but returns an `AsyncIterable` of execution results |
| 20 | +instead of a single response: |
| 21 | + |
| 22 | +```js |
| 23 | +import { subscribe, parse } from 'graphql'; |
| 24 | +import schema from './schema.js'; |
| 25 | + |
| 26 | +const document = parse(` |
| 27 | + subscription { |
| 28 | + messageSent |
| 29 | + } |
| 30 | +`); |
| 31 | + |
| 32 | +const iterator = await subscribe({ schema, document }); |
| 33 | + |
| 34 | +for await (const result of iterator) { |
| 35 | + console.log(result); |
| 36 | +} |
| 37 | +``` |
| 38 | + |
| 39 | +Each time your application publishes a new `messageSent` event, the iterator emits a new result. It's up to your transport layer to manage the connection and forward these updates to the client. |
| 40 | + |
| 41 | +## When to use subscriptions |
| 42 | + |
| 43 | +Subscriptions are helpful when your application needs to respond to real-time events. For example: |
| 44 | + |
| 45 | +- Receiving new messages in a chat |
| 46 | +- Updating a live feed or activity stream |
| 47 | +- Displaying presence indicators (e.g., "user is online") |
| 48 | +- Reflecting real-time price changes |
| 49 | + |
| 50 | +If real-time delivery isn’t essential, consider using polling with queries instead. Subscriptions require more infrastructure and introduce additional complexity, especially around scaling and connection management. |
| 51 | + |
| 52 | +## Implementing subscriptions in GraphQL.js |
| 53 | + |
| 54 | +GraphQL.js supports subscription execution, but you’re responsible for setting up the transport and event system. At a minimum, you’ll need: |
| 55 | + |
| 56 | +- A `Subscription` root type in your schema |
| 57 | +- A `subscribe` resolver that returns an `AsyncIterable` |
| 58 | +- An event-publishing mechanism |
| 59 | +- A transport layer to maintain the connection |
| 60 | + |
| 61 | +The following examples use the [`graphql-subscriptions`](https://github.com/apollographql/graphql-subscriptions) package to set up an in-memory event system. |
| 62 | + |
| 63 | +### Install dependencies |
| 64 | + |
| 65 | +Start by installing the necessary packages: |
| 66 | + |
| 67 | +```bash |
| 68 | +npm install graphql graphql-subscriptions |
| 69 | +``` |
| 70 | + |
| 71 | +To serve subscriptions over a network, you’ll also need a transport implementation. One option is [`graphql-ws`](https://github.com/enisdenjo/graphql-ws), a community-maintained WebSocket library. This guide focuses on schema-level implementation. |
| 72 | + |
| 73 | +### Set up a pub/sub instance |
| 74 | + |
| 75 | +Create a `PubSub` instance to manage your in-memory event system: |
| 76 | + |
| 77 | +```js |
| 78 | +import { PubSub } from 'graphql-subscriptions'; |
| 79 | + |
| 80 | +const pubsub = new PubSub(); |
| 81 | +``` |
| 82 | + |
| 83 | +This `pubsub` object provides `publish` and `asyncIterator` methods, allowing |
| 84 | +you to broadcast and listen for events. |
| 85 | + |
| 86 | +### Define a subscription type |
| 87 | + |
| 88 | +Next, define your `Subscription` root type. Each field on this type should return |
| 89 | +an `AsyncIterable`. Subscribe functions can also accept standard resolver arguments |
| 90 | +such as `args`, `context`, and `info`, depending on your use case: |
| 91 | + |
| 92 | +```js |
| 93 | +import { GraphQLObjectType, GraphQLSchema, GraphQLString } from 'graphql'; |
| 94 | + |
| 95 | +const SubscriptionType = new GraphQLObjectType({ |
| 96 | + name: 'Subscription', |
| 97 | + fields: { |
| 98 | + messageSent: { |
| 99 | + type: GraphQLString, |
| 100 | + subscribe: () => pubsub.asyncIterator(['MESSAGE_SENT']), |
| 101 | + }, |
| 102 | + }, |
| 103 | +}); |
| 104 | +``` |
| 105 | + |
| 106 | +This schema defines a `messageSent` field that listens for the `MESSAGE_SENT` event and returns a string. |
| 107 | + |
| 108 | +### Publish events |
| 109 | + |
| 110 | +You can trigger a subscription event from any part of your application using the |
| 111 | +`publish` method: |
| 112 | + |
| 113 | +```js |
| 114 | +pubsub.publish('MESSAGE_SENT', { messageSent: 'Hello world!' }); |
| 115 | +``` |
| 116 | + |
| 117 | +Clients subscribed to the `messageSent` field will receive this message. |
| 118 | + |
| 119 | +### Construct your schema |
| 120 | + |
| 121 | +Finally, build your schema and include the `Subscription` type: |
| 122 | + |
| 123 | +```js |
| 124 | +const schema = new GraphQLSchema({ |
| 125 | + subscription: SubscriptionType, |
| 126 | +}); |
| 127 | +``` |
| 128 | + |
| 129 | +A client can then initiate a subscription like this: |
| 130 | + |
| 131 | +```graphql |
| 132 | +subscription { |
| 133 | + messageSent |
| 134 | +} |
| 135 | +``` |
| 136 | + |
| 137 | +Whenever your server publishes a `MESSAGE_SENT` event, clients subscribed to |
| 138 | +`messageSent` will receive the updated value over their active connection. |
| 139 | + |
| 140 | +## Planning for production |
| 141 | + |
| 142 | +The in-memory `PubSub` used in this example is suitable for development only. |
| 143 | +It does not support multiple server instances or distributed environments. |
| 144 | + |
| 145 | +For production, consider using a more robust event system such as: |
| 146 | + |
| 147 | +- Redis Pub/Sub |
| 148 | +- Message brokers like Kafka or NATS |
| 149 | +- Custom implementations with persistent queues or durable event storage |
| 150 | + |
| 151 | +Subscriptions also require careful handling of connection limits, authentication, rate limiting, and network reliability. These responsibilities fall to your transport layer and infrastructure. |
0 commit comments