Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1---
2title: Document Caching
3order: 4
4---
5
6# Document Caching
7
8By default, `urql` uses a concept called _Document Caching_. It will avoid sending the same requests
9to a GraphQL API repeatedly by caching the result of each query.
10
11This works like the cache in a browser. `urql` creates a key for each request that is sent based on
12a query and its variables.
13
14The default _document caching_ logic is implemented in the default `cacheExchange`. We'll learn more
15about ["Exchanges" on the "Architecture" page.](../architecture.md)
16
17## Operation Keys
18
19
20
21Once a result comes in it's cached indefinitely by its key. This means that each unique request
22can have exactly one cached result.
23
24However, we also need to invalidate the cached results so that requests are sent again and updated,
25when we know that some results are out-of-date. With document caching we assume that a result may
26be invalidated by a mutation that executes on data that has been queried previously.
27
28In GraphQL the client can request additional type information by adding the `__typename` field to a
29query's _selection set_. This field returns the name of the type for an object in the results, and
30we use it to detect commonalities and data dependencies between queries and mutations.
31
32
33
34In short, when we send a mutation that contains types that another query's results contains as well,
35that query's result is removed from the cache.
36
37This is an aggressive form of cache invalidation. However, it works well for content-driven sites,
38while it doesn't deal with normalized data or IDs.
39
40## Request Policies
41
42The _request policy_ that is defined will alter what the default document cache does. By default, the
43cache will prefer cached results and will otherwise send a request, which is called `cache-first`.
44In total there are four different policies that we can use:
45
46- `cache-first` (the default) prefers cached results and falls back to sending an API request when
47 no prior result is cached.
48- `cache-and-network` returns cached results but also always sends an API request, which is perfect
49 for displaying data quickly while keeping it up-to-date.
50- `network-only` will always send an API request and will ignore cached results.
51- `cache-only` will always return cached results or `null`.
52
53The `cache-and-network` policy is particularly useful, since it allows us to display data instantly
54if it has been cached, but also refreshes data in our cache in the background. This means though
55that `fetching` will be `false` for cached results although an API request may still be ongoing in
56the background.
57
58For this reason there's another field on results, `result.stale`, which indicates that the cached
59result is either outdated or that another request is being sent in the background.
60
61[Read more about which request policies are available in the API
62docs.](../api/core.md#requestpolicy-type)
63
64## Document Cache Gotchas
65
66This cache has a small trade-off! If we request a list of data, and the API returns an empty list,
67then the cache won't be able to see the `__typename` of said list and invalidate it.
68
69There are two ways to fix this issue, supplying `additionalTypenames` to the context of your query or [switch to "Normalized Caching"
70instead](../graphcache/normalized-caching.md).
71
72### Adding typenames
73
74This will elaborate about the first fix for empty lists, the `additionalTypenames`.
75
76Example where this would occur:
77
78```js
79const query = `query { todos { id name } }`;
80const result = { todos: [] };
81```
82
83At this point we don't know what types are possible for this query, so a best practice when using
84the default cache is to add `additionalTypenames` for this query.
85
86```js
87// Keep the reference stable.
88const context = useMemo(() => ({ additionalTypenames: ['Todo'] }), []);
89const [result] = useQuery({ query, context });
90```
91
92Now the cache will know when to invalidate this query even when the list is empty.
93
94We may also use this feature for mutations, since occasionally mutations must invalidate data that
95isn't directly connected to a mutation by a `__typename`.
96
97```js
98const [result, execute] = useMutation(`mutation($name: String!) { createUser(name: $name) }`);
99
100const onClick = () => {
101 execute({ name: 'newName' }, { additionalTypenames: ['Wallet'] });
102};
103```
104
105Now our `mutation` knows that when it completes it has an additional type to invalidate.