Robert Zhu (@rbzhu is a software engineer at Facebook, working on GraphQL. Before, Robert worked at companies like Microsoft and even founded his own company. This talk explains the What, Why and How of Subscriptions, how to build your own GraphQL subscriptions server and the differences to live queries.
A song of data and mutations
The talk starts with a demo of GraphiQL exploring a Game of Thrones API:
Executing this query returns a list of houses, their name, amount of banner men and signature words.
- we sent a request containing a document, got a response (JSON)
- GraphQL Language Specification (domain agnostic)
- Schema (domain specific)
- Express (Node.js HTTP server)
- GraphQL Server (GraphQL-js)
The request/response lifecycle of this operation is straight forward:
- Request: Client sends server a GraphQL Document
- Execute: Server parses, validates, and executes the document
- Response: Server sends response back to client
What about writes?
Writes in GraphQL are called "mutations" and work with the same request/response lifecycle. They also always return a value.
What about real-time data?
Given that we can mutate values, how can we build an application that displays real-time changes?
Enter GraphQL subscriptions, a third type of operation like
If the operation is a subscription, the result is an event stream called the "Response Stream" where each event in the event stream is the result of executing the operation for each new event on an underlying "Source Stream".
Executing a subscription creates a persistent function on the server that maps an underlying Source Stream to a returned Response Stream.
Imagine we had a microservice architecture like this:
The different microservices are wired together through an event bus.
Now lets imagine we get a new requirement: We want to detect when a suspicious activity occurs. So we add a new microservice: the Alarm microservice. The Alarm microservice acts as a subscriber to the other microservices. For example, whenever a login occurs, it can inspect that login event - triggering an alarm e.g. when a login fails repeatedly from an unusual location.
On the GraphQL level, the GraphQL query causes the subscription service to derive the set of domain-specific events from the GraphQL document and subscribe to the event bus for these events. This forms a "source stream". Whenever an event arrives from this "source stream", the subscription service combines it with the document and executes the resulting query. The result is sent back to the client along the "response stream".
The lifecycle after the subscription service stays very similar to the usual request/response lifecycle.
Managing the subscription state
Subscriptions introduce a certain complexity to the stack:
- Who subscribed?
- What document/variables did they send?
- How do we send messages back to the client?
- What events does this set of document/variables map to?
- Who is listening to what events?
The solution to this differ depending on the scale:
|Streaming Transport||WebSockets, SSE, HTTP long polling, RSocket, MQTT||(same)|
|Connection Handling||In process/Middleware||Gateway/Proxy|
|Source Stream||EventEmitter, .NET Events||Redis, Kafka, SQS, Pusher|
|Subscription Session Data||In-memory collection, file||Database, Cache|
Subscriptions at Facebook
If you are using Facebook, you are using GraphQL subscriptions. These Facebook features are powered by GraphQL Subscriptions:
- Live Likes
- Live Comments
- Typing Indicators
- Live Video Streaming Reactions
- Live Video Comments
- ...and many more
What about live queries?
The difference between Subscriptions and live queries is the difference between observing state and observing events. An example for observing state is observing updates to a value that was 10 before and knowing it is now 11. An example for observing an event is observing the event of increments to the value, and knowing that the value that was 10 before was just incremented by 1.
|Know why data changed||Convert any existing query|
|Fine control over publish||Simple fallback (polling)|
|No server-side changes|
|Streaming support||Streaming support|
|Reactive API/Language||Reactive API/Language|
|Streaming/event system||Reactive data layer|