Docs
Launch GraphOS Studio

Subscriptions

Get real-time updates from your GraphQL server


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

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

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 .

When to use subscriptions

In the majority of cases, your client should not use 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 for the following:

  • Small, incremental changes to large objects. Repeatedly polling for a large object is expensive, especially when most of the object's rarely change. Instead, you can fetch the object's initial state with a , 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.

Supported subscription protocols

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

  • WebSocket, using one of the following subprotocols:
  • HTTP, using chunked multipart responses (Apollo Client 3.7.11 and later)

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

WebSocket subprotocols

The first popular JavaScript library to implement 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 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 with a 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! automatically sends the required headers with the request if the terminating HTTPLink is passed a .

Usage with Relay or urql

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

Relay
import { createFetchMultipartSubscription } from "@apollo/client/utilities/subscriptions/relay";
import { Environment, Network, RecordSource, Store } from "relay-runtime";
const fetchMultipartSubs = createFetchMultipartSubscription(
"https://api.example.com"
);
const network = Network.create(fetchQuery, fetchMultipartSubs);
export const RelayEnvironment = new Environment({
network,
store: new Store(new RecordSource()),
});

urql

import { createFetchMultipartSubscription } from "@apollo/client/utilities/subscriptions/urql";
import { Client, fetchExchange, subscriptionExchange } from "@urql/core";
const url = "https://api.example.com";
const multipartSubscriptionForwarder = createFetchMultipartSubscription(
url
);
const client = new Client({
url,
exchanges: [
fetchExchange,
subscriptionExchange({
forwardSubscription: multipartSubscriptionForwarder,
}),
],
});

Defining a subscription

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

Server side

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

type Subscription {
commentAdded(postID: ID!): Comment
}

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

Client side

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

const COMMENTS_SUBSCRIPTION = gql`
subscription OnCommentAdded($postID: ID!) {
commentAdded(postID: $postID) {
id
content
}
}
`;

When executes the OnCommentAdded , it establishes a connection to your and listens for response data. Unlike with a , 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 does push data to a subscribing client, that data conforms to the structure of the executed , just like it does for a :

{
"data": {
"commentAdded": {
"id": "123",
"content": "What a thoughtful and well written post!"
}
}
}

WebSocket setup

1. Install required libraries

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

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

npm install graphql-ws

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

index.js
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
const wsLink = new GraphQLWsLink(createClient({
url: 'ws://localhost:4000/subscriptions',
}));

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

Although can use your GraphQLWsLink to execute all types, in most cases it should continue using HTTP for queries and . 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 being executed.

index.js
import { split, HttpLink } from '@apollo/client';
import { getMainDefinition } from '@apollo/client/utilities';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
const httpLink = new HttpLink({
uri: 'http://localhost:4000/graphql'
});
const wsLink = new GraphQLWsLink(createClient({
url: 'ws://localhost:4000/subscriptions',
}));
// The split function takes three parameters:
//
// * A function that's called for each operation to execute
// * The Link to use for an operation if the function returns a "truthy" value
// * The Link to use for an operation if the function returns a "falsy" value
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink,
);

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

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

index.js
import { ApolloClient, InMemoryCache } from '@apollo/client';
// ...code from the above example goes here...
const client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache()
});

If you provide the link option, it takes precedence over the uri option (uri sets up a default HTTP link chain using the provided URL).

5. Authenticate over WebSocket (optional)

It is often necessary to authenticate a client before allowing it to receive results. To do this, you can provide a connectionParams option to the GraphQLWsLink constructor, like so:

import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
const wsLink = new GraphQLWsLink(createClient({
url: 'ws://localhost:4000/subscriptions',
connectionParams: {
authToken: user.authToken,
},
}));

Your GraphQLWsLink passes the connectionParams object to your server whenever it connects. Your server receives the connectionParams object and can use it to perform authentication, along with any other connection-related tasks.

Subscriptions via multipart HTTP

No additional libraries or configuration are required. adds the required headers to your request when the default terminating HTTPLink receives a at the uri specified when initializing the link or instance.

Note: in order to use over multipart HTTP in a React Native application, additional configuration is required. See the React Native docs for more information.

Executing a subscription

You use 's useSubscription Hook to execute a from React. Like useQuery, useSubscription returns an object from that contains loading, error, and data properties you can use to render your UI.

The following example component uses the we defined earlier to render the most recent comment that's been added to a specified blog post. Whenever the pushes a new comment to the client, the component re-renders with the new comment.

const COMMENTS_SUBSCRIPTION = gql`
subscription OnCommentAdded($postID: ID!) {
commentAdded(postID: $postID) {
id
content
}
}
`;
function LatestComment({ postID }) {
const { data, loading } = useSubscription(
COMMENTS_SUBSCRIPTION,
{ variables: { postID } }
);
return <h4>New comment: {!loading && data.commentAdded.content}</h4>;
}

Subscribing to updates for a query

Whenever a returns a result in , that result includes a subscribeToMore function. You can use this function to execute a followup that pushes updates to the 's original result.

The subscribeToMore function is similar in structure to the fetchMore function that's commonly used for handling pagination. The primary difference is that fetchMore executes a followup query, whereas subscribeToMore executes a .

As an example, let's start with a standard that fetches all of the existing comments for a given blog post:

const COMMENTS_QUERY = gql`
query CommentsForPost($postID: ID!) {
post(postID: $postID) {
comments {
id
content
}
}
}
`;
function CommentsPageWithData({ params }) {
const result = useQuery(
COMMENTS_QUERY,
{ variables: { postID: params.postID } }
);
return <CommentsPage {...result} />;
}

Let's say we want our to push an update to our client as soon as a new comment is added to the post. First we need to define the that will execute when the COMMENTS_QUERY returns:

const COMMENTS_SUBSCRIPTION = gql`
subscription OnCommentAdded($postID: ID!) {
commentAdded(postID: $postID) {
id
content
}
}
`;

Next, we modify our CommentsPageWithData function to add a subscribeToNewComments property to the CommentsPage component it returns. This property is a function that will be responsible for calling subscribeToMore after the component mounts.

function CommentsPageWithData({ params }) {
const { subscribeToMore, ...result } = useQuery(
COMMENTS_QUERY,
{ variables: { postID: params.postID } }
);
return (
<CommentsPage
{...result}
subscribeToNewComments={() =>
subscribeToMore({
document: COMMENTS_SUBSCRIPTION,
variables: { postID: params.postID },
updateQuery: (prev, { subscriptionData }) => {
if (!subscriptionData.data) return prev;
const newFeedItem = subscriptionData.data.commentAdded;
return Object.assign({}, prev, {
post: {
comments: [newFeedItem, ...prev.post.comments]
}
});
}
})
}
/>
);
}

In the example above, we pass three options to subscribeToMore:

  • document indicates the to execute.
  • variables indicates the to include when executing the .
  • updateQuery is a function that tells how to combine the 's currently cached result (prev) with the subscriptionData that's pushed by our . The return value of this function completely replaces the current cached result for the .

Finally, in our definition of CommentsPage, we tell the component to subscribeToNewComments when it mounts:

export function CommentsPage({subscribeToNewComments}) {
useEffect(() => subscribeToNewComments(), []);
return <>...</>
}

useSubscription API reference

Note: If you're using React Apollo's Subscription render prop component, the option/result details listed below are still valid (options are component props and results are passed into the render prop function). The only difference is that a subscription prop (which holds a parsed into an AST by gql) is also required.

Options

The useSubscription Hook accepts the following options:

Other

client (optional)

ApolloClient<object>

An ApolloClient instance. By default useSubscription / Subscription uses the client passed down via context, but a different client can be passed in.

DefaultContext

Shared context between your component and your network interface ().

How you want your component to interact with the Apollo cache. For details, see Setting a fetch policy.

onComplete (optional)

Since 3.7.0

() => void

Allows the registration of a callback function that will be triggered each time the useSubscription Hook / Subscription component completes the .

(options: OnDataOptions<TData>) => any

Allows the registration of a callback function that will be triggered each time the useSubscription Hook / Subscription component receives data. The callback options object param consists of the current instance in client, and the received data in data.

(error: ApolloError) => void

Allows the registration of a callback function that will be triggered each time the useSubscription Hook / Subscription component receives an error.

boolean | ((options: BaseSubscriptionOptions<TData, TVariables>) => boolean)

Determines if your should be unsubscribed and subscribed again when an input to the hook (such as subscription or variables) changes.

Determines if the current should be skipped. Useful if, for example, depend on previous queries and are not ready yet.

An object containing all of the your needs to execute

⚠️ Deprecated

Use onComplete instead

Allows the registration of a callback function that will be triggered when the useSubscription Hook / Subscription component completes the .

(options: OnSubscriptionDataOptions<TData>) => any

⚠️ Deprecated

Use onData instead

Allows the registration of a callback function that will be triggered each time the useSubscription Hook / Subscription component receives data. The callback options object param consists of the current instance in client, and the received data in subscriptionData.

Result

After being called, the useSubscription Hook returns a result object with the following properties:

Other

An object containing the result of your . Defaults to an empty object.

ApolloError

A runtime error with graphQLErrors and networkError properties

boolean

A boolean that indicates whether any initial data has been returned

The older subscriptions-transport-ws library

If your server uses subscriptions-transport-ws instead of the newer graphql-ws library, you need to make a few changes to how you set up your link:

  1. Instead of npm install graphql-ws:

    npm install subscriptions-transport-ws
  2. Instead of import { createClient } from 'graphql-ws':

    import { SubscriptionClient } from 'subscriptions-transport-ws'
  3. Instead of import { GraphQLWsLink } from '@apollo/client/link/subscriptions':

    import { WebSocketLink } from '@apollo/client/link/ws'
  4. The options you pass to new SubscriptionClient differ slightly from those passed to createClient:

    • The first passed to the SubscriptionClient constructor is the URL for your server.
    • The connectionParams option is nested under an options object called options instead of being at the top level. (You can also pass the new SubscriptionClient constructor directly to new WebSocketLink.)
    • See the subscriptions-transport-ws README for complete SubscriptionClient API docs.

After you create your wsLink, everything else in this article still applies: useSubscription, subscribeToMore, and split links work exactly the same way for both implementations.

The following is an example of a typical WebSocketLink initialization:

import { WebSocketLink } from "@apollo/client/link/ws";
import { SubscriptionClient } from "subscriptions-transport-ws";
const wsLink = new WebSocketLink(
new SubscriptionClient("ws://localhost:4000/subscriptions", {
connectionParams: {
authToken: user.authToken
}
})
);

More details on WebSocketLink's API can be found in its API docs.

Previous
Refetching
Next
Fragments
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company