Subscriptions

Get real-time updates from your GraphQL server


In addition to queries and mutations, GraphQL supports a third operation type: subscriptions.

Like queries, subscriptions enable you to fetch data. Unlike queries, subscriptions are long-lasting operations that can change their result over time. They can maintain an active connection to your GraphQL server (most commonly via WebSocket), enabling the server to push updates to the subscription's result.

Subscriptions are useful for notifying your client in real time about changes to back-end data, such as the creation of a new object or updates to an important field.

When to use subscriptions

In the majority of cases, your client should not use subscriptions to stay up to date with your backend. Instead, you should poll intermittently with queries, or re-execute queries on demand when a user performs a relevant action (such as clicking a button).

You should use subscriptions for the following:

  • Small, incremental changes to large objects. Repeatedly polling for a large object is expensive, especially when most of the object's fields rarely change. Instead, you can fetch the object's initial state with a query, and your server can proactively push updates to individual fields as they occur.

  • Low-latency, real-time updates. For example, a chat application's client wants to receive new messages as soon as they're available.

Note: Subscriptions cannot be used to listen to local client events, like subscribing to changes in the cache. Subscriptions are intended to be used to subscribe to external data changes, and have those received changes be stored in the cache. You can then leverage Apollo Client's observability model to watch for changes in the cache, using client.watchQuery or useQuery.

Supported subscription protocols

The GraphQL spec does not define a specific protocol for sending subscription requests. Apollo Client supports the following protocols for subscriptions:

You must use the same protocol as the GraphQL endpoint you're communicating with.

WebSocket subprotocols

The first popular JavaScript library to implement subscriptions over WebSocket is called subscriptions-transport-ws. This library is no longer actively maintained. Its successor is a library called graphql-ws. These two libraries do not use the same WebSocket subprotocol, so you need to use the same subprotocol that your GraphQL endpoint uses.

The WebSocket setup section below uses graphql-ws. If your endpoint uses subscriptions-transport-ws, see this section for differences in configuration.

Note: Confusingly, the subscriptions-transport-ws library calls its WebSocket subprotocol graphql-ws, and the graphql-ws library calls its subprotocol graphql-transport-ws! In this article, we refer to the two libraries (subscriptions-transport-ws and graphql-ws), not the two subprotocols.

HTTP

To use Apollo Client with a GraphQL endpoint that supports multipart subscriptions over HTTP, make sure you're using version 3.7.11 or later.

Aside from updating your client version, no additional configuration is required! Apollo Client automatically sends the required headers with the request if the terminating HTTPLink is passed a subscription operation.

Usage with Relay or urql

To consume a multipart subscription over HTTP in an app using Relay or urql, Apollo Client provides network layer adapters that handle the parsing of the multipart response format.

Relay
TypeScript
1import { createFetchMultipartSubscription } from "@apollo/client/utilities/subscriptions/relay";
2import { Environment, Network, RecordSource, Store } from "relay-runtime";
3
4const fetchMultipartSubs = createFetchMultipartSubscription(
5  "https://api.example.com"
6);
7
8const network = Network.create(fetchQuery, fetchMultipartSubs);
9
10export const RelayEnvironment = new Environment({
11  network,
12  store: new Store(new RecordSource()),
13});

urql

TypeScript
1import { createFetchMultipartSubscription } from "@apollo/client/utilities/subscriptions/urql";
2import { Client, fetchExchange, subscriptionExchange } from "@urql/core";
3
4const url = "https://api.example.com";
5
6const multipartSubscriptionForwarder = createFetchMultipartSubscription(
7  url
8);
9
10const client = new Client({
11  url,
12  exchanges: [
13    fetchExchange,
14    subscriptionExchange({
15      forwardSubscription: multipartSubscriptionForwarder,
16    }),
17  ],
18});

Defining a subscription

You define a subscription on both the server side and the client side, just like you do for queries and mutations.

Server side

You define available subscriptions in your GraphQL schema as fields of the Subscription type. The following commentAdded subscription notifies a subscribing client whenever a new comment is added to a particular blog post (specified by postID):

GraphQL
1type Subscription {
2  commentAdded(postID: ID!): Comment
3}

For more information on implementing support for subscriptions on the server side, see the Apollo Server documentation for subscriptions.

Client side

In your application's client, you define the shape of each subscription you want Apollo Client to execute, like so:

TypeScript
JavaScript
1const COMMENTS_SUBSCRIPTION: TypedDocumentNode<
2  OnCommentAddedSubscription,
3  OnCommentAddedSubscriptionVariables
4> = gql`
5  subscription OnCommentAdded($postID: ID!) {
6    commentAdded(postID: $postID) {
7      id
8      content
9    }
10  }
11`;

When Apollo Client executes the OnCommentAdded subscription, it establishes a connection to your GraphQL server and listens for response data. Unlike with a query, there is no expectation that the server will immediately process and return a response. Instead, your server only pushes data to your client when a particular event occurs on your backend.

Whenever your GraphQL server does push data to a subscribing client, that data conforms to the structure of the executed subscription, just like it does for a query:

JSON
1{
2  "data": {
3    "commentAdded": {
4      "id": "123",
5      "content": "What a thoughtful and well written post!"
6    }
7  }
8}

WebSocket setup

1. Install required libraries

Apollo Link is a library that helps you customize Apollo Client's network communication. You can use it to define a link chain that modifies your operations and routes them to the appropriate destination.

To execute subscriptions over WebSocket, you can add a GraphQLWsLink to your link chain. This link requires the graphql-ws library. Install it like so:

Bash
1npm install graphql-ws

Import and initialize a GraphQLWsLink object in the same project file where you initialize ApolloClient:

TypeScript
index.ts
1import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
2import { createClient } from 'graphql-ws';
3
4const wsLink = new GraphQLWsLink(createClient({
5  url: 'ws://localhost:4000/subscriptions',
6}));

Replace the value of the url option with your GraphQL server's subscription-specific WebSocket endpoint. If you're using Apollo Server, see Setting a subscription endpoint.

Although Apollo Client can use your GraphQLWsLink to execute all operation types, in most cases it should continue using HTTP for queries and mutations. This is because queries and mutations don't require a stateful or long-lasting connection, making HTTP more efficient and scalable if a WebSocket connection isn't already present.

To support this, the @apollo/client library provides a split function that lets you use one of two different Links, according to the result of a boolean check.

The following example expands on the previous one by initializing both a GraphQLWsLink and an HttpLink. It then uses the split function to combine those two Links into a single Link that uses one or the other according to the type of operation being executed.

TypeScript
index.ts
1import { split, HttpLink } from '@apollo/client';
2import { getMainDefinition } from '@apollo/client/utilities';
3import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
4import { createClient } from 'graphql-ws';
5
6const httpLink = new HttpLink({
7  uri: 'http://localhost:4000/graphql'
8});
9
10const wsLink = new GraphQLWsLink(createClient({
11  url: 'ws://localhost:4000/subscriptions',
12}));
13
14// The split function takes three parameters:
15//
16// * A function that's called for each operation to execute
17// * The Link to use for an operation if the function returns a "truthy" value
18// * The Link to use for an operation if the function returns a "falsy" value
19const splitLink = split(
20  ({ query }) => {
21    const definition = getMainDefinition(query);
22    return (
23      definition.kind === 'OperationDefinition' &&
24      definition.operation === 'subscription'
25    );
26  },
27  wsLink,
28  httpLink,
29);

Using this logic, queries and mutations will use HTTP as normal, and subscriptions will use WebSocket.

After you define your link chain, you provide it to Apollo Client via the link constructor option:

TypeScript
JavaScript
index.ts
1import { ApolloClient, InMemoryCache } from '@apollo/client';
2
3// ...code from the above example goes here...
4
5const client = new ApolloClient({
6  link: splitLink,
7  cache