Apollo Graphql comes with built-in support for caching which can be used as a state management system (which they recommend). There are a couple of different ways to update cache after an update in this state management system:
- Refetch query
- Built-in normalization
- Update callback
This post will teach you when and how to do the different kinds of cache update strategies and show you some util functions to make cache updates as easy as a one-liner.
After executing a mutation and changing something on the server, chances are you’re going to need to refresh the UI. And a quick solution is to use the refetchQueries API.
For example: With refreshQueries you specify one or more queries in order to refresh the parts that have been affected by the mutation.
While this works, it has a big disadvantage attached to it, in that we need to make multiple network trips to render our UI. This extra round trip also occurs even if we have the data on the client.
The Apollo Client is an abstraction on top of normalized data and cache and this facade provides access to the underlying data. This is similar to how databases handle access to data. This storage facade can intercept queries and automatically remove duplication. It can also update the cache automatically, though this depends on the type of mutation.
When dealing with any type of data that needs to be stored somewhere, we need to keep the amount of duplicate data saved to an absolute minimum. Ideally, the goal here is no duplicate data.
This facade provides an API layer that is much easier to use. The API’s allow us to:
- Configure/design the client-side data using cache policies.
- Query data using client.readQuery or client.watchQuery.
- Mutate data using useMutation.
The Apollo Client provides a set of tools to interact with the cached data and all of this comes out of the box and ready to use. By minimizing direct access to the actual data using the facade/API enables data normalization, behind the scenes.
How the Apollo Client normalises data is:
- By splitting the results into individual objects.
- Assign a logically unique identifier to each object, so the cache is able to keep track of the object.
- Storing objects in a flattened structure.
You can learn more about how GraphQL does normalization here.
When we perform an add, remove or reorder items that are cached, the Apollo Client is not able to automatically update the cache. We need to write an update function to instruct the cache on how we want to update it.
There are some rules that we can follow so we know when we need to create our own update function. These rules are as follows:
- If the side-effect we want to occur has nothing to do with the return data.
- We can’t return the entire set of changed objects.
- The mutation changes the ordering.
- The mutation adds or removes items.
A side-effect would be to clear the cache after a mutation, for example logging out of a system. We get a successful response from the mutation, but we don’t need to cache anything. For this we can use the client.clearStore() method in the update function.
We need to return the entire list of objects that were changed for the automatic update to work (using the normalization).
To update the cache if we cannot return the entire set, we use the update function:
To remove an item from the cache filter out the deleted item.
Notice how we first remove the entity from the query and then from the normalized cache using
To add an item to the cache, get the existing entities from the readQuery and use the spread operator ([…]) to append the new course to the objects in cache.
Now we know what we need to do and how to do it, let’s see how we can make it simpler to do these cache updates.
Making Cache Updates Easy
As you saw in the previous section, doing the manual updates can be quite tedious. For that reason, I have created some utils for doing these updates as a one-liner.
First we define an interface with an id, as this is the convention we will be using to know the property to get the entity’s id from.
From here we can create a function to handle adding new items to the cache.
This will read the query and add the new entity to the cache.
And the same for deleting:
This will read the query and remove the entity from the cache.
These helpers can be used like this:
In this post, we learned about the various ways that the Apollo Client handles updates to the cache. We looked at how the Apollo Client handles automatic cache updates using normalization, refetch queries, and manual updates of the cahce.
We finished off by showing you how to simplify the manual update process by creating a function to handle the update and deletes, so these become an easy-to-use one line of code.