w3resource

Configuring the cache


Apollo Client uses a normalized, in-memory cache to dramatically speed up the execution of queries that don't rely on real-time data. In this tutorial, we will discuss cache setup and configuration.

Installation

The InMemoryCache class resides in a different package from the Apollo Client core. Hence ensure that apollo-cache-inmemory package is installed in your project using the command:

npm install apollo-cache-inmemory -save

Initializing the cache

To initialize cache, create an InMemoryCache object and provide it to the ApolloClient constructor as shown in the code snippet below:

import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import { ApolloClient } from 'apollo-client';

const client = new ApolloClient({
  link: new HttpLink(),
  cache: new InMemoryCache()
});

The InMemoryCache constructor accepts a variety of options described in Configuring the cache.

Cache Configuration

You can provide a configuration object to the InMemoryCache constructor to customize its behavior. This object supports the following fields:

NAME TYPE DESCRIPTION
addTypename boolean Indicates whether to add __typename to the document (default: true)
dataIdFromObject function A function that takes a data object and returns a unique identifier to be used when normalizing the data in the store.
fragmentMatcher object By default, the InMemoryCache uses a heuristic fragment matcher. If you are using fragments on unions and interfaces, you will need to use an IntrospectionFragmentMatcher.

cacheRedirects object A map of functions to redirect a query to another entry in the cache before a request takes place. This is useful if you have a list of items and want to use the data from the list query on a detail page where you're querying an individual item.

Data normalization

The InMemoryCache normalizes query results before saving them to the cache by:

  1. Splitting the results into individual objects
  2. Assigning a unique identifier to each object
  3. Storing the objects in a flattened data structure

Assigning unique identifiers

Default identifiers

By default, the InMemoryCache attempts to generate a unique identifier for an object by combining the object's __typename field with its id or _id field.

If an object doesn't specify a __typename or one of id or _id, InMemoryCache falls back to using the object's path within its associated query (e.g., ROOT_QUERY.allPeople.0 for the first record returned for an allPeople root query). Avoid this fallback strategy whenever possible, because it scopes cached objects to individual queries. This means that if multiple queries all return the same object, each query inefficiently caches a separate instance of that object.

Warning: Each object type you cache should either always include an id field or never include an id field. InMemoryCache throws an error if it encounters an inconsistency in the presence or absence of this field for a particular type.

Custom identifiers

You can define a custom strategy for generating unique identifiers for cached objects. To do so, provide the dataIdFromObject configuration option to the InMemoryCache constructor. This option is a function that takes in an object and returns a unique identifier for that object.

For example, if your object types all define a key field that you want to use as a unique identifier, you could define dataIdFromObject like so:

const cache = new InMemoryCache({
  dataIdFromObject: object => object.key || null
});

Note that InMemoryCache uses the exact string that dataIdFromObject returns. If you want the unique identifier to include the object's __typename field, you must include it as part of the function's logic.

You can use different logic to generate unique identifiers for each of your object types by keying off of an object's __typename property, like so:


import { InMemoryCache, defaultDataIdFromObject } from 'apollo-cache-inmemory';
const cache = new InMemoryCache({
  dataIdFromObject: object => {
    switch (object.__typename) {
      case 'foo': return object.key; // use the `key` field as the identifier
      case 'bar': return `bar:${object.blah}`; // append `bar` to the `blah` field as the identifier
      default: return defaultDataIdFromObject(object); // fall back to default handling
    }
  }
});

Automatic cache updates

Let's look at a case where just using the cache normalization results in the correct update to our store. Let's say we perform the following query:

{
  post(id: '5') {
    id
    score
  }
}

Then, we perform the following mutation:

mutation {
  upvotePost(id: '5') {
    id
    score
  }
}

If the id field on both results matches up, then the score field everywhere in our UI will be updated automatically! One nice way to take advantage of this property as much as possible is to make your mutation results have all of the data necessary to update the queries previously fetched. A simple trick for this is to use "fragments" discussed in the previous tutorial to share fields between the query and the mutation that affects it.

Previous: Error handling
Next: Interacting with cached data