2 Years of GraphQL in Production
By: Stein Janssen
Tags: Programming, JavaScript, Software Development, Learning, GraphQL

Stitching

Being able to fetch all the data from one endpoint sounds nice, but stitching can also lead to some very nasty issues. Let’s take a look at the following example:

We have a front end that communicates with the Public API. The Public API stitches the Order API, which stitches the Product API. The Public API is the only API that can be accessed by the front end. This chaining of stitching might not seem like a very big issue, but if you have something like 20 APIs with multiple layers, releasing can become a nightmare.

Why? Because when you update the schema of the Product API, you will need to restart the Order API and the Public API (in that sequence), as the schemas need to be reloaded. Having to restart a dozen APIs every time you update the GraphQL schema will definitely not be very fun.

Another issue that we can encounter is when we want to query up instead of down — so from a product to its parent order. This won’t work because the Order API will only start when the Product API is running, as it needs to load its schema.

That means that we can fetch the products that belong to an order:

order {

identifier

products {

identifier

}

}

But we can’t fetch the order that belongs to a certain product:

products {

identifier

order {

identifier

}

}

To solve these issues, we can rearrange how we stitch our APIs. By making the Public API responsible for loading in all the schemas, we only need to restart one API when a schema is updated.

Because the Public API now fetches all schemas, we can add some code that will extend the product schema with the order property. This will make it possible to fetch the order through the product query. Check out the Apollo documentation for more information about extending schemas.

Now, not all queries and mutations should be accessible through our Public API. For example, we don’t want to give our customers the possibility to trigger a mutation that will change the payment status. One way to solve this is by filtering out certain queries and mutations. We could loop over all the queries and mutations in the schemas and compare them with a list. If the queries are on the list, they will be visible. If not, they will be removed from the schema. Another option would be to add a middleware that checks if the current user has the right access to trigger certain queries and mutations.

We chose a combination of those two options. But now we have another issue. Because not all of our mutations are accessible through the Public API anymore, we will need to call the Payment API directly when updating the payment status. This will work for mutations, but it won’t work for all of our queries, as the child and parent objects are only stitched in the Public API. To solve this, we have to rearrange our setup one more time.

We will create a Gateway API and make it responsible for stitching all the schemas. The Public API will stitch the Gateway API and remove all queries and mutations that should not be accessible through the front end. We can now use the Gateway API when we need to do queries or mutations in the back end, as our back-end services are located in the same network.