GraphQL Cache Updates Made Easy

Share on facebook
Share on google
Share on twitter
Share on linkedin

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.

Refetch query

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.

For example:

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.

Built-in normalisation

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.

Update callback

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 evict.

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.

Do you want to become an Angular architect? Check out Angular Architect Accelerator.

Related Posts and Comments

How to Handle Errors in a Reactive Angular App

In this post, we will cover how to handle errors in a reactive Angular app. To provide a good user experience you should always let your users know what state your application is in. That includes showing a loading spinner when it’s loading and showing error messages if there are any errors. It is a

Read More »

How to Set Up Git Hooks in an Nx Repo

Git hooks can be used to automate tasks in your development workflow. The earlier a bug is discovered, the cheaper it is to fix (and the less impact it has). Therefore it can be helpful to run tasks such as linting, formatting, and tests when you are e.g. committing and pushing your code, so any

Read More »

The Stages of an Angular Architecture with Nx

Long gone are the times when the frontend was just a dumb static website. Frontend apps have gotten increasingly complex since the rise of single-page application frameworks like Angular. It comes with the price of increased complexity and the ever-changing frontend landscape requires you to have an architecture that allows you to scale and adapt

Read More »

The Best Way to Use Signals in Angular Apps

Since Angular 16, Angular now has experimental support for signals and there is a lot of confusion in the community about whether this is going to replace RxJS or how it should be used in an app in combination with RxJS. This blog post sheds some light on what I think is the best way

Read More »

High ROI Testing with Cypress Component Testing

Testing is one of the most struggled topics in Angular development and many developers are either giving up testing altogether or applying inefficient testing practices consuming all their precious time while giving few results in return. This blog post will change all this as we will cover how I overcame these struggles the hard way

Read More »