I always ask people…
Do you really need real-time?
This question really boils down to your data-access patterns in product. If most of your specifications are static lists or content, then having a simple request and response transport coupled with a caching client can serve most of your needs.
This is what is I love about GraphQL technologies, they are optimized for the majority use case and give you alternative methods to keeping data fresh:
- Client Cache Invalidation
Previous Real Time Experience
Coming from the Meteor community previously, data access was “real-time” via a piece of technology called DDP (Distributed Data Protocol). This allowed the engineer to create a subscription on the client for pieces of data, sent over WebSockets via “live-queries”.
This real-time solution works great for experiences demanding real-time feature sets, and having your data refresh on screen in response to any document changes is magical, but it leads to tons of waste. In this pub sub system you would, using Mongo Syntax, enforce the data over the wire via “projections”. A client developer would specify fields they need to render UIs and the system will react to any changes to the data in those underlying fields.
This is where it gets suboptimal. In many user interfaces there are a collection of data points used to present information that is static. So if most of your data is static, you incur a higher cost than its worth with live-queries.
Ideally, we only want to update the piece of state we as product developers deem necessary to keep the data and UX consistent. This is where event based subscriptions come in.
The most popular way to do subscriptions in GraphQL today is via an Event Based Subscription. Let’s look at how this happens.
The current state of the art around GraphQL Subscriptions center around a topic-based publish/subscribe model. “Messages” are published to “topics” or “channels”. Subscribers in these systems receive all messages to which they subscribe and all subscribers receive the same messages. The publisher, our server, is responsible for defining these topics and the types of messages that are published to them.
The idea here is to create topics around state mutations, and publish messages along with metadata to subscribers. Our main subscriber, the client, will get a message and “react” to the message with some work.
We can enhance simple request/response GraphQL queries by subscribing to topics that mutate the underlying data backed by the query and merge changes to into the UI.
The full server repo can be found here.
Today I’d like to walk through a really simple use case for a GraphQL Subscription. The most “real-time” feature I could think of would be Messaging. So let’s build a little Chat Service and in the next chapter we’ll build the UI!
To get started let’s add a Subscription Type to our GraphQL type definitions.
We have defined a Subscription named
messageAdded which we will execute whenever a message has been added to the chatroom.
Next given this base set of resolvers, we are going to add a subscription resolver:
Subscriptions are another ROOT_TYPE in GraphQL. To get situated with subscriptions we need to install the
yarn add graphql-subscriptions -S
Next we’re going to create our Publisher.
graphql-subscriptions comes with a nice event emitter to get yourself acquainted with subscriptions, but it is advised you back this via a more production grade solution. My personal favorite is:
graphql-redis-subscriptions - A graphql subscriptions implementation using redis and apollo's graphql-subscriptions
For now we’ll go with the
PubSub class shipped with
It is important to note that unlike Queries and Mutations, Subscriptions resolvers are not functions. They are maps with a subscribe method and return an AsyncIterable. We use pubsub’s asyncIterator to map over the topics available and pick the
messageAdded channel. We then use
withFilter to make sure that this channel only publishes data in the current chatroom. We do this by looking at the chatroomId incoming from metadata and the chatroomId supplied by the subscription. Filters leave room for tons of customizability for subscriptions, I encourage you to play with it!
Great! Our GraphQL server is well aware that messageAdded is a subscription. We now need to add the hooks in triggering the publish of a specific topic.
We’ll use the pubsub’s
publish method to publish metadata to a specific topic. In this case,
When a message is created via our
addMessage mutation, we publish to the
messageAdded channel the metadata of the message just created.
The last thing we need to do is expose a WebSocket that clients can connect to. This will allow us to push data to these clients in response to our event based subscriptions.
Here we take our express server and bind it to a httpServer from
node. We then create our SubscriptionServer.
We bind our
- GraphQL Schema
graphql/execution. This is responsible for the execution phase of fulfilling a GraphQL request.
graphql/subscriptionThis is responsible for subscribing to updates on specific data.
- server — the websocket server created
- path (optional) an optional path for the WS url.
With that in place we are ready to connect our client!
Tune into the next post to see how the UI fits into our newly added Subscriptions! With the Subscriptions RFC merged into GraphQL, we are going to see a lot of movement in this space for the coming future. I’d keep a close eye on developments, and pay special attention to Urigo as he is really championing this space for the Angular community!
graphql - GraphQL is a query language and execution engine tied to any backend service.