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.

<Callout

⚠️

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.

For example, in the Facebook schema, we have a mutation field named storyLike, that clients can use to like a post. The client might want to refetch the like count, as well as the like sentence (“Dan and 3 others like this”. We do this translation on the server because of the complexity of that translation in various languages). To do so, they would issue the following mutation:

mutation StoryLikeMutation($input: StoryLikeInput) {
storyLike(input: $input) {
story {
likers { count }
likeSentence { text }
}
}
}

But when you’re looking at a post, you also want to get pushed an update whenever someone else likes the post! That’s where subscriptions come in; the Facebook schema has a subscription field named storyLikeSubscribe that allows the client to get pushed data anytime someone likes or unlikes that story! The client would create a subscription like this:

subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) {
storyLikeSubscribe(input: $input) {
story {
likers { count }
likeSentence { text }
}
}
}

The client would then send this subscription to the server, along with the value for the $input variable, which would contain information like the story ID to which we are subscribing:

input StoryLikeSubscribeInput {
storyId: string
clientSubscriptionId: string
}

Why not Live Queries?

Notably, this approach requires the client to subscribe to events that it cares about. Another approach is to have the client subscribe to a query, and ask for updates every time the result of that query changes. Why didn’t we take that approach?

Let’s look back at the data we wanted to refetch for the story:

fragment StoryLikeData on Story {
story {
likers { count }
likeSentence { text }
}
}

What events could trigger that a change to the data fetched in that fragment?

  • Someone likes the post.
  • Someone unlikes the post.
  • Someone who had liked the post deactivates their account (changes the like count down one, changes the like sentence to decrement the translated count).
  • Someone who had liked the post reactivates their account (changes the like count up one, changes the like sentence to increment the translated count).
  • Someone who had liked the post blocks you (cannot show them in the like sentence).
  • Someone who had liked the post changes their name (need to update the text of the like sentence).
  • Our internal ranking model for the ordering of names in the like sentence updates, and we should be listing a different person first (want to update the text of the like sentence).

And that’s just the tip of the iceberg in terms of events; each of those events also becomes tricky when there are thousands of people subscribed, and millions of people who liked the post. Implementing live queries for this set of data proved to be immensely complicated.