Overview
The router can also play the role of client—when it sends individual, smaller queries to each subgraph. Accordingly, it can also use APQ to shorten its requests to subgraphs.
In this lesson, we will:
- Configure the router to send APQ to subgraphs
- Test out APQ in a request to the listings subgraph
Sending APQ to subgraphs
We can use the router-config.yaml
file to customize how the router handles APQ with its subgraphs.
We'll need a top-level apq
key, with another key nested below called subgraph
. (If you added a router
key from the previous step, the subgraph
key can sit on the same level.)
apq:subgraph:router: # optional configuration for client APQ behavior
Under the subgraph
key, we can specify settings for all
subgraphs, or override the rules for a specific subgraph. Let's try this out, disabling APQ globally while enabling it for a single subgraph: listings
.
apq:subgraph:all:enabled: falsesubgraphs:listings:enabled: truerouter: # router configuration
Then, restart the router to make use of these new settings; make sure to include the --log
flag, and pass it a value of trace
.
APOLLO_KEY=<APOLLO_KEY> \APOLLO_GRAPH_REF=<APOLLO_GRAPH_REF> \./router --config router-config.yaml \--log trace
Now, when communicating with the listings
subgraph, the router can send it the operation identifier of the smaller operation string it would normally send. Provided that the listings
subgraph server is configured to understand this hash and use it correctly, that's all that's needed! And in our case, because the listings
subgraph is powered by Apollo Server, it can handle the APQ without any trouble at all.
Let's return to the query plan we inspected in the last lesson. It consists of two components that can run in sequence: one query sent to the listings
subgraph first, followed by a query to reviews
.
QueryPlan {Sequence {Fetch(service: "listings") {{listing(id: $listingId) {__typenameidtitledescriptionnumOfBeds}}},Flatten(path: "listing") {Fetch(service: "reviews") {{... on Listing {__typenameid}} =>{... on Listing {overallRatingreviews {idtext}}}},},},}
Let's zoom in on the request to listings
. For the given $listingId
, the listings
service needs to provide a number of fields.
{listing(id: $listingId) {__typenameidtitledescriptionnumOfBeds}}
Return to Explorer, and let's rerun this query to see what happens under the hood of the router.
query GetListingAndReviews($listingId: ID!) {listing(id: $listingId) {titledescriptionnumOfBedsoverallRatingreviews {idtext}}}
And in the Variables panel.
{"listingId": "listing-1"}
After running the query, return to the router's terminal. We could scroll through its output to find the exact line we're looking for, but let's expedite things by searching directly for the following line, which references the SHA-256 hash generated for the query being sent to listings
.
\"sha256Hash\":\"8c6389470a25e2b57141aeb1aee624f77b8310e90563a17a11ebced119756fe8\\"
This brings us to a specific bit of output that starts with "our JSON body:". Here we can find both the query string, along with the SHA-256 hash that was generated for it.
our JSON body: "{\"query\":\"query GetListingAndReviews__listings__0($listingId:ID!){listing(id:$listingId){__typename id title description numOfBeds}}\",\"operationName\":\"GetListingAndReviews__listings__0\",\"variables\":{\"listingId\":\"listing-1\"},\"extensions\":{\"persistedQuery\":{\"version\":1,\"sha256Hash\":\"8c6389470a25e2b57141aeb1aee624f77b8310e90563a17a11ebced119756fe8\"}}}"
The first time the router sends this request to the listings
subgraph, it includes the full operation string along with the hash. This enables us to send just the hash on subsequent requests.
We'll send the listings
service another separate request to demonstrate this. Open a new terminal, and build the following curl
command to the listings
service at https://rt-airlock-subgraphs-listings.herokuapp.com/
. This time, we won't send the query string—just the hash. (But note that we do still need to provide a value for our variable, listingId
!)
curl --get https://rt-airlock-subgraphs-listings.herokuapp.com/ \--header 'content-type: application/json' \--data-urlencode 'variables={"listingId":"listing-1"}' \--data-urlencode 'extensions={"persistedQuery":{"version":1,"sha256Hash":"8c6389470a25e2b57141aeb1aee624f77b8310e90563a17a11ebced119756fe8"}}'
Note: To provide the variables object for our query, we use an additional --data-urlencode
flag that sets the variables
property with the appropriate value.
Now that the listings
service has already received the operation string this hash refers to, it knows exactly what to do the second time we send it. The hash, along with the appropriate variables, is enough to get data back!
{"data": {"listing": {"__typename": "Listing","id": "listing-1","title": "Cave campsite in snowy MoundiiX","description": "Enjoy this amazing cave campsite in snow MoundiiX, where you'll be one with the nature and wildlife in this wintery planet. All space survival amenities are available. We have complementary dehydrated wine upon your arrival. Check in between 34:00 and 72:00. The nearest village is 3AU away, so please plan accordingly. Recommended for extreme outdoor adventurers.","numOfBeds": 2}}}
And because we used query variables ($listingId
) in our original operation, we can change the value we provide on the fly; the same hash will continue to work.
curl --get https://rt-airlock-subgraphs-listings.herokuapp.com/ \--header 'content-type: application/json' \--data-urlencode 'variables={"listingId":"listing-3"}' \--data-urlencode 'extensions={"persistedQuery":{"version":1,"sha256Hash":"8c6389470a25e2b57141aeb1aee624f77b8310e90563a17a11ebced119756fe8"}}'
You might have also noticed router logs detailing the component of the query that was sent to the reviews
subgraph: this request did NOT include a hash of the query. Per our configuration, we enabled APQ exclusively for the listings
subgraph!
Practice
Key takeaways
- The router can also use APQ in the queries it sends to subgraphs. This feature can be enabled globally or on an individual subgraph basis. Subgraphs built with Apollo Server support APQ out of the box.
Up next
With automatic persisted queries, we can reduce the amount of data we send over the wire; but caching them still relies upon the router's (or subgraph server's) in-memory cache. In the next lesson, we'll take our caching to the next level with a distributed cache multiple router instances can share.
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.