Since 3.9.0

Memory management

Learn how to choose and set custom cache sizes


Cache Sizes

For better performance, Apollo Client caches (or, in other words, memoizes) many internally calculated values. In most cases, these values are cached in weak caches , which means that if the source object is garbage-collected, the cached value will be garbage-collected, too.

These caches are also Least Recently Used (LRU) caches, meaning that if the cache is full, the least recently used value will be garbage-collected.

Depending on your application, you might want to tweak the cache size to fit your needs.

You can set your cache size before (recommended) or after loading the Apollo Client library.

Setting cache sizes before loading the Apollo Client library

Setting cache sizes before loading the Apollo Client library is recommended because some caches are already initialized when the library is loaded. Changed cache sizes only affect caches created after the fact, so you'd have to write additional runtime code to recreate these caches after changing their size.

TypeScript
1import type { CacheSizes } from '@apollo/client/utilities';
2
3globalThis[Symbol.for("apollo.cacheSize")] = {
4 parser: 100,
5 "fragmentRegistry.lookup": 500
6} satisfies Partial<CacheSizes>

Adjusting cache sizes after loading the Apollo Client library

You can also adjust cache sizes after loading the library.

JavaScript
1import { cacheSizes } from '@apollo/client/utilities';
2import { print } from '@apollo/client'
3
4cacheSizes.print = 100;
5// cache sizes changed this way will only take effect for caches
6// created after the cache size has been changed, so we need to
7// reset the cache for it to be effective
8
9print.reset();

Choosing appropriate cache sizes

All configurable caches hold memoized values. If an item is cache-collected, it incurs only a small performance impact and doesn't cause data loss. A smaller cache size might save you memory.

You should choose cache sizes appropriate for storing a reasonable number of values rather than every value. To prevent too much recalculation, choose cache sizes that are at least large enough to hold memoized values for all hooks/queries on the screen at any given time.

To choose good sizes for our memoization caches, you need to know what they use as source values, and have a general understanding of the data flow inside of Apollo Client.

For most memoized values, the source value is a parsed GraphQL document— a DocumentNode. There are two types:

  • User-supplied DocumentNodes are created by the user, for example by using the gql template literal tag. This is the QUERY, MUTATION, or SUBSCRIPTION argument passed into a useQuery hook or as the query option to client.query.

  • Transformed DocumentNodes are derived from user-supplied DocumentNodes, for example, by applying DocumentTransforms to them.

As a rule of thumb, you should set the cache sizes for caches using a transformed DocumentNode at least to the same size as for caches using a user-supplied DocumentNode. If your application uses a custom DocumentTransform that does not always transform the same input to the same output, you should set the cache size for caches using a Transformed DocumentNode to a higher value than for caches using a user-supplied DocumentNode.

By default, Apollo Client uses a base value of 1000 cached objects for caches using user-supplied DocumentNode instances, and scales other cache sizes relative to that. For example, the default base value of 1000 for user-provided DocumentNodes would scale to 2000, 4000, etc. for transformed DocumentNodes, depending on the transformation performed.

This base value should be plenty for most applications, but you can tweak them if you have different requirements.

Measuring cache usage

Since estimating appropriate cache sizes for your application can be hard, Apollo Client exposes an API for cache usage measurement.
This way, you can click around in your application and then take a look at the actual usage of the memoizing caches.

Keep in mind that this API is primarily meant for usage with the Apollo DevTools (an integration is coming soon), and the API may change at any point in time.
It is also only included in development builds, not in production builds.

 note
The cache usage API is only meant for manual measurements. Don't rely on it in production code or tests.
TypeScript
1console.log(client.getMemoryInternals())
2

Logs output in the following JSON format:

Read more...
JSON
1{
2 limits:     {
3   parser: 1000,
4   canonicalStringify: 1000,
5   print: 2000,
6   'documentTransform.cache': 2000,
7   'queryManager.getDocumentInfo': 2000,
8   'PersistedQueryLink.persistedQueryHashes': 2000,
9   'fragmentRegistry.transform': 2000,
10   'fragmentRegistry.lookup': 1000,
11   'fragmentRegistry.findFragmentSpreads': 4000,
12   'cache.fragmentQueryDocuments': 1000,
13   'removeTypenameFromVariables.getVariableDefinitions': 2000,
14   'inMemoryCache.maybeBroadcastWatch': 5000,
15   'inMemoryCache.executeSelectionSet': 10000,
16   'inMemoryCache.executeSubSelectedArray': 5000
17 },
18 sizes: {
19   parser: 26,
20   canonicalStringify: 4,
21   print: 14,
22   addTypenameDocumentTransform: [
23     {
24       cache: 14,
25     },
26   ],
27   queryManager: {
28     getDocumentInfo: 14,
29     documentTransforms: [
30       {
31         cache: 14,
32       },
33       {
34         cache: 14,
35       },
36     ],
37   },
38   fragmentRegistry: {
39     findFragmentSpreads: 34,
40     lookup: 20,
41     transform: 14,
42   },
43   cache: {
44     fragmentQueryDocuments: 22,
45   },
46   inMemoryCache: {
47     executeSelectionSet: 4345,
48     executeSubSelectedArray: 1206,
49     maybeBroadcastWatch: 32,
50   },
51   links: [
52     {
53       PersistedQueryLink: {
54         persistedQueryHashes: 14,
55       },
56     },
57     {
58       removeTypenameFromVariables: {
59         getVariableDefinitions: 14,
60       },
61     },
62   ],
63 },
64}
65

Cache options

Other

Cache size for the getFragmentDoc method of ApolloCache .

This function is called with user-provided fragment definitions.

Read more...

This function is called from readFragment with user-provided fragment definitions.

Cache size for the cache of DocumentTransform instances with the cache option set to true.

Can be called with user-defined or already-transformed DocumentNodes.

Read more...

The cache size here should be chosen with other DocumentTransforms in mind. For example, if there was a DocumentTransform that would take x DocumentNodes, and returned a differently-transformed DocumentNode depending if the app is online or offline, then we assume that the cache returns 2*x documents. If that were concatenated with another DocumentTransform that would also duplicate the cache size, you'd need to account for 4*x documents returned by the second transform.

Due to an implementation detail of Apollo Client, if you use custom document transforms you should always add n (the "base" number of user-provided Documents) to the resulting cache size.

If we assume that the user-provided transforms receive n documents and return n documents, the cache size should be 2*n.

If we assume that the chain of user-provided transforms receive n documents and return 4*n documents, the cache size should be 5*n.

This size should also then be used in every other cache that mentions that it operates on a "transformed" DocumentNode.

Cache size for the findFragmentSpreads method of FragmentRegistry .

This function is called with transformed DocumentNodes, as well as recursively with every fragment spread referenced within that, or a fragment referenced by a fragment spread.

Read more...

Note: This function is a dependency of fragmentRegistry.transform, so having too small of cache size here might involuntarily invalidate values in the transform cache.

A cache inside of FragmentRegistry .

This function is called with fragment names in the form of a string.

Read more...

The size of this case should be chosen with the number of fragments in your application in mind.

Note: This function is a dependency of fragmentRegistry.transform, so having too small of a cache size here might involuntarily invalidate values in the transform cache.

A cache inside of FragmentRegistry .

Can be called with user-defined or already-transformed DocumentNodes.

Cache size for the executeSelectionSet method on StoreReader .

Note: executeSelectionSet will be set to the resultCacheMaxSize option and will fall back to this configuration value if the option is not set.

Read more...

Every object that is read from the cache will be cached here, so it is recommended to set this to a high value.

Cache size for the executeSubSelectedArray method on StoreReader .

Note: executeSubSelectedArray will be set to the resultCacheMaxSize option and will fall back to this configuration value if the option is not set.

Read more...

Every array that is read from the cache will be cached here, so it is recommended to set this to a high value.

Cache size for the maybeBroadcastWatch method on InMemoryCache .

Note: maybeBroadcastWatch will be set to the resultCacheMaxSize option and will fall back to this configuration value if the option is not set.

Read more...

This method is used for dependency tracking in the InMemoryCache and prevents from unnecessary re-renders. It is recommended to keep this value significantly higher than the number of possible subscribers you will have active at the same time in your application at any time.

A cache inside of PersistedQueryLink .

It is called with transformed DocumentNodes.

Read more...

This cache is used to cache the hashes of persisted queries.

A cache inside of QueryManager .

It is called with transformed DocumentNodes.

Cache used in removeTypenameFromVariables .

This function is called transformed DocumentNodes.

Cache used by canonicalStringify .

Read more...

This cache contains the sorted keys of objects that are stringified by canonicalStringify. It uses the stringified unsorted keys of objects as keys. The cache will not grow beyond the size of different object shapes encountered in an application, no matter how much actual data gets stringified.

number

Cache size for the parser function.

It is called with user-provided DocumentNodes.

Read more...

This method is called by HOCs and hooks.

number

Cache size for the print function.

It is called with transformed DocumentNodes.

Read more...

This method is called to transform a GraphQL query AST parsed by gql back into a GraphQL string.