Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
at main 105 lines 4.5 kB view raw view rendered
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![Keys for GraphQL Requests](../assets/urql-operation-keys.png) 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![Document Caching](../assets/urql-document-caching.png) 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.