Overview
Before we can send subscription operations through our federated graph, we need to tell our locally-running router (part of the rover dev
process) exactly how it should handle subscription events.
In this lesson, we will:
- Learn about the HTTP callback event loop
- Use the
router-config.yaml
file to configure the router for subscriptions over HTTP callback
Subscriptions and router-config.yaml
To enable subscriptions in our federated graph, we'll turn back to the router-config.yaml
file. This is the configuration file where we can provide all kinds of details about how the router should run—including how it should handle subscription events, and which protocol to use for which subgraphs.
Let's open up router/router-config.yaml
.
First, we'll add a top-level key called subscription
. On the level below this (indented in), we'll define two extra properties: enabled
, which we'll set to true
, and mode
.
# ...other router config propertiessubscription:enabled: truemode: # TODO
Beneath the mode
key, we'll specify some additional properties. This is where we can enable one of two subscription methods: WebSockets (denoted as passthrough
) and HTTP callbacks (denoted as callback
). We'll stick with callback
for this course.
Note: To explore all possible configuration options in the router.yaml
file, check out the official documentation.
subscription:enabled: truemode:callback:
Next, we'll add the remaining properties that nest beneath the callback
property, and then walk through them one-by-one.
subscription:enabled: truemode:callback:public_url: http://127.0.0.1:4000/callbacklisten: 127.0.0.1:4000path: /callbackheartbeat_interval: 15ssubgraphs:- messages
public_url
The public_url
property represents the router's public URL that subgraphs must be able to send new subscription data to. This is the "callback URL" that the router sends to the subgraph. Our locally running router runs on http://127.0.0.1:4000
, and we've appended /callback
as the specific endpoint the subgraphs should use when "calling back" to the router (this is the default value the router will use unless we provide a different value).
listen
The listen
property specifies the IP address and port the router will listen on for subscription callbacks. We haven't changed it from the default value here; but note that for security reasons, it's best to expose a separate port that's available only through your internal network.
path
The path
property states the specific path of our router's callback endpoint. It should match the value appended to the public_url
. If you don't specify a path
here, it takes the value of /callback
by default.
heartbeat_interval
The hearbeat_interval
property specifies an interval at which the subgraph should "check in" with the router. By default, the router asks the subgraph to send a "check" message every five seconds.
Note: The heartbeat_interval
property is optional. You might not want your subgraph constantly checking in—particularly if your architecture follows a pay-by-function-call model! You can disable these check-ins by passing disabled
in place of the number of seconds.
subgraphs
Lastly, under subgraphs
, we specify the names of the subgraphs that use the HTTP callback protocol to provide subscription data. We have just one: messages
.
The subscription initialization
Let's see what these new configuration properties mean for our graph. We'll walk through the subscription setup process using an example operation from earlier.
subscription SubscribeToMessagesInConversation($listenForMessageInConversationId: ID!) {listenForMessageInConversation(id: $listenForMessageInConversationId) {textsentTime}}
When the router receives this operation, it creates the query plan and determines that the entire operation is the responsibility of the messages
subgraph. So, it sends it onward—including extra information about how to call back. Here's what this looks like.
{"query": "subscription SubscribeToMessagesInConversation(){...}", // remainder of subscription operation"extensions": {"subscription": {"callbackUrl": "http://127:0.0.1:4000/callback/randomly-assigned-subscription-hash","subscriptionId": "randomly-assigned-subscription-hash","verifier": "XYZ", // a unique property"heartbeatIntervalMs": 15000}}}
That's a lot of helpful data! Let's take a closer look at what we're working with here.
In addition to the subscription operation itself, the object contains the callbackUrl
and the heartbeatIntervalMs
values that we provided in the router-config.yaml
(with the exception of seconds being converted to milliseconds). Additionally, the router will assign a subscriptionId
for the very specific operation it sends to the subgraph.
Finally, we have a property called verifier
. The verifier
is used to verify the subgraph's identity with the router every time it sends a new message—it's how the router knows who it's talking to and whether the source is legitimate.
Upon receiving this big object from the router, the subgraph includes the verifier
in its first response back to the router to make contact. The router responds with confirmation, and the subgraph initializes the subscription by sending back an empty GraphQL object. With that, setup is complete! And we're ready for updates.
The subscription loop
With our subscription setup complete, the loop begins. Here's what happens under the hood:
- Every five seconds, the subgraph takes on its emitter role and sends the router a
"check"
message. Kind of like saying "Are you still there?" every once in awhile—if the router responds, the loop continues. - When the subgraph detects a subscription event (new data!) it sends the router a
"next"
message containing the data. - If something goes wrong or the subscription should be terminated, the subgraph sends a
"complete"
message and the router ends the subscription.
You don't need to do anything special to handle these "check"
, "next"
and "complete"
messages. This is handled out-of-the-box by our Apollo Server implementations. These additional details can serve as a guide for anyone working on an implementation of the HTTP Callback Protocol.
If we do nothing else in our graph, the subgraph and router will continue checking in every five seconds. But as soon as we trigger some change to our pertinent data, the subgraph has something substantial to share. The router receives this new data, packages it up with anything else it needed to fetch across the graph, and returns it all to the client. And the loop goes on!
Re-running rover dev
We've provided a bunch of configuration for our locally-running router to use; now let's just restart the rover dev
process and make sure that we're actually using it!
APOLLO_KEY="..." \APOLLO_GRAPH_REF="..." \rover dev \--supergraph-config supergraph.yaml \--router-config router-config.yaml
Run this command, passing in your own values for APOLLO_GRAPH_REF
and APOLLO_KEY
. And with that, we should have success!
==> your supergraph is running! head to http://localhost:4000 to query your supergraph
Practice
Drag items from this box to the blanks above
a long-lived, persistent connection
heartbeat_interval
checkin_interval
required
BFF
a callback URL
optional
authorization
emitter
verifier
communicator
Key takeaways
- The HTTP callback protocol allows the router and subgraph server to stay in contact, checking in periodically and sharing updates as they happen.
- This protocol can optionally include a third mechanism, the emitter, which can take on the responsibility of checking in with the router as well as conveying new data.
- The router provides the subgraph server with a callback URL where the subgraph (or emitter) can send its messages when checking in and sharing updates.
- The router also provides a verifier, a unique value that the subgraph should include with every communication to verify its identity.
Up next
Our router process is ready to handle our subscriptions—now let's finish up our logic and start sending messages!
Share your questions and comments about this lesson
This course is currently in
You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.