Docs
Launch GraphOS Studio

Caching in Apollo Client

Overview


stores the results of your queries in a local,

, in-memory cache. This enables to respond almost immediately to queries for already-cached data, without even sending a network request.

For example, the first time your app executes a GetBook for a Book object with id 5, the flow looks like this:

GraphQL ServerInMemoryCacheApollo ClientGraphQL ServerInMemoryCacheApollo ClientBook:5 not foundin cacheBook:5 is cachedGetBook(bookId: "5")Query sent to serverServer respondswith BookReturns Book

And each later time your app executes GetBook for that same object, the flow looks like this instead:

GraphQL ServerInMemoryCacheApollo ClientGraphQL ServerInMemoryCacheApollo ClientBook:5 foundin cache!(Server is never queried)GetBook(bookId: "5")Returns Book

The cache is highly configurable. You can customize its behavior for individual types and in your schema, and you can even use it to store and interact with local data that isn't fetched from your .

How is data stored?

's InMemoryCache stores data as a flat lookup table of objects that can reference each other. These objects correspond to the objects that are returned by your queries. A single cached object might include returned by multiple queries, if those queries fetch different of the same object.

The cache is flat, but objects returned by a often aren't! In fact, their nesting can be arbitrarily deep. Take a look at this example response:

{
"data": {
"person": {
"__typename": "Person",
"id": "cGVvcGxlOjE=",
"name": "Luke Skywalker",
"homeworld": {
"__typename": "Planet",
"id": "cGxhbmV0czox",
"name": "Tatooine"
}
}
}
}

This response contains a Person object, which in turn contains a Planet object in its homeworld .

So how does the InMemoryCache store nested data in a flat lookup table? Before storing this data, the cache needs to normalize it.

Data normalization

Whenever the cache receives response data, it does the following:

1. Identify objects

First, the cache identifies all of the distinct objects included in a response. In

, there are two objects:

  • A Person with id cGVvcGxlOjE=
  • A Planet with id cGxhbmV0czox

2. Generate cache IDs

After identifying all objects, the cache generates a cache ID for each one. A cache ID uniquely identifies a particular object while it's in the InMemoryCache.

By default, an object's cache ID is the concatenation of the object's __typename and id (or _id) , separated by a colon (:).

So, the default cache IDs for the objects in

are:

  • Person:cGVvcGxlOjE=
  • Planet:cGxhbmV0czox

You can customize the cache ID format for a particular . See

.

If the cache can't generate a cache ID for a particular object (for example, if no id or _id is present), that object is cached directly inside its parent object, and it must be referenced via the parent (this means the cache isn't always completely flat).

3. Replace object fields with references

Next, the cache takes each that contains an object and replaces its value with a reference to the appropriate object.

For example, here's the Person object from the example above before reference replacement:

{
"__typename": "Person",
"id": "cGVvcGxlOjE=",
"name": "Luke Skywalker",
"homeworld": {
"__typename": "Planet",
"id": "cGxhbmV0czox",
"name": "Tatooine"
}
}

And here's that same object after replacement:

{
"__typename": "Person",
"id": "cGVvcGxlOjE=",
"name": "Luke Skywalker",
"homeworld": {
"__ref": "Planet:cGxhbmV0czox"
}
}

The homeworld now contains a reference to the appropriate normalized Planet object.

This replacement does not occur for a particular object if

failed to generate a cache ID for that object. Instead, the original object remains.

Later, if you for another Person who has the same homeworld, that normalized Person object will contain a reference to the same cached object! can dramatically reduce data duplication, and it also helps your local data stay up to date with your server.

4. Store normalized objects

Finally, the resulting objects are all stored in the cache's flat lookup table.

Whenever an incoming object has the same cache ID as an existing cached object, the of those objects are merged:

  • If the incoming object and the existing object share any , the incoming object overwrites the cached values for those .
  • that appear in only the existing object or only the incoming object are preserved.

constructs a partial copy of your on your client, in a format that's optimized for reading and updating as your app's state changes.

Visualizing the cache

To help understand the structure of your cached data, we strongly recommend installing the

.

This browser extension includes an inspector that enables you to view all of the normalized objects contained in your cache:

The Cache tab of the Apollo Client Devtools

Example

Let's say we use to run the following on the

:

query {
allPeople(first:3) { # Return the first 3 items
people {
id
name
homeworld {
id
name
}
}
}
}

This returns the following result of three Person objects, each with a corresponding homeworld (a Planet object):

Notice that each object in the result includes a __typename , even though our string didn't include this . That's because automatically queries for every object's __typename.

After the result is cached, we can view the state of our cache in the Devtools:

The Cache tab of the Apollo Client Devtools

Our cache now contains five normalized objects (in addition to the ROOT_QUERY object): three Person objects and two Planet objects.

Why do we only have two Planet objects? Because two of the three returned Person objects have the same homeworld. By

like this, can cache a single copy of an object, and multiple other objects can include references to it (see the __ref of the object in the screenshot above).

Next steps

Now that you have a basic understanding of how 's cache works, learn more about how to

.

Then, you can learn how to

directly, without executing a against your server. This is a powerful option for
local state management
.

Previous
Best practices
Next
Configuration
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company