In Apollo Client 4, how do the cache-first, cache-and-network, network-only and no-cache fetch policies differ for an issue list, and when do you pick each?
answer
- where the first answer comes from
- default reads the cache, fetches on a miss
- cached rows now, server rows next
- one policy never writes or watches
- nextFetchPolicy governs later executions
basics
~20 sApollo Client 4 defaults to cache-first: serve a complete cached result, fetch only on a miss. cache-and-network shows cached rows and always fetches; network-only always fetches but still writes and watches the cache; no-cache bypasses the cache entirely.
solid answer
~40 s`fetchPolicy` decides where a `useQuery` result comes from. `cache-first`, the default, returns a complete cached result without a request and fetches only when fields are missing. It suits an issue list kept current by normalized mutation results. `cache-and-network` renders cached rows at once with `loading: true` and always sends a request, which suits a shared list other people edit. `network-only` skips the cache read, but it writes the response and then reacts to later cache writes, like a triage view that must open fresh. `no-cache` neither writes nor watches the cache, so a later `closeIssue` never updates it. Keep it for one-off data such as an export preview. `cache-only` never fetches, and `standby` parks a query without running it. `nextFetchPolicy` sets the policy for later executions, for example `cache-and-network` first and `cache-first` after.
go deeper
Know that cache-first is the default and that fetchPolicy decides whether useQuery reads the cache, the network, or both. Name one case where you would switch away from the default.
Explain all four main policies in terms of reading, fetching, writing and watching the cache, and why no-cache lists ignore mutation results. Justify a policy for a concrete screen.
Show you know cache writes re-read cache-first rather than refetching, and how nextFetchPolicy and variable changes interact. Pick policies per screen from how often the server data changes underneath users.
Set an app-wide default through defaultOptions and decide who may deviate from it, weighing request volume against staleness complaints. Tie that choice to how the team handles refresh on focus and polling.
## What a fetch policy controls Every `useQuery` call in Apollo Client 4 creates a watched query, and its **`fetchPolicy`** answers three questions each time that query executes. Does it read the normalized cache first? Does it send a network request? Is the response written to the cache? A fourth follows from the answers: does the query re-render later when some other operation, such as a mutation, writes to the same cache records? The default is **`cache-first`**. You change it per hook with `useQuery(ISSUES, { fetchPolicy: "cache-and-network" })`, or for every watched query through `defaultOptions.watchQuery` on the client. ## The six values side by side | Policy | Reads cache first | Sends a request | Writes the result | Re-renders on later cache writes | |---|---|---|---|---| | `cache-first` (default) | yes | only if data is missing | yes | yes | | `cache-and-network` | yes | always | yes | yes | | `network-only` | no | always | yes | yes, after the first result | | `no-cache` | no | always | no | no | | `cache-only` | yes | never | not applicable | yes | | `standby` | no | not on its own | not applicable | no | `cache-and-network` and `standby` are only valid for watched queries such as `useQuery`. Mutations accept just `network-only`, which is the default and writes the result, and `no-cache`, which does not. ## Choosing one for the issue tracker - **`cache-first`** fits the main issue list when your mutations return the fields they change. `closeIssue` returning `{ id status }` updates the cached `Issue` record, and the list re-renders without a request. The risk is showing data that changed on the server behind your back until something refetches it. - **`cache-and-network`** fits a list that other people edit at the same time. Navigating back shows the cached rows instantly, and the request that always follows brings in teammates' changes. You pay one request per execution, and the component must tolerate `loading: true` alongside rows. - **`network-only`** fits a screen that must never open on old data, such as a triage queue at the start of a shift. It shows a loading state first, then behaves like a cached query, because its result is written and watched. - **`no-cache`** fits data that must not be shared, like a one-off export preview or a sensitive report. Its result is not written, and later cache writes are ignored, so a `closeIssue` elsewhere never updates it. - **`cache-only`** fits a view that must never cause traffic. When the data is missing it returns `data: undefined` with `dataState: "empty"` and `loading: false`. - **`standby`** holds a query without running it. It is what `skipToken` uses under the hood. ## A cache write does not trigger a request A common belief is that `cache-and-network` or `network-only` hits the network every time the cache changes. Apollo Client 4 does not do that. When a mutation writes to records a watched query reads, the query re-reads the cache with a temporary `cache-first` policy. It goes to the network only if the cache can no longer satisfy the query, for example because a field is now missing. The policy's "always fetch" applies when the query executes: on mount, on a variables change, or on an explicit `refetch()`. ## nextFetchPolicy and variable changes `fetchPolicy` governs the first execution. **`nextFetchPolicy`** changes what happens afterwards: 1. The query runs with `fetchPolicy`, say `cache-and-network`. 2. After that fetch, Apollo sets the query's policy to `nextFetchPolicy`, say `cache-first`, so later executions of that watched query are served from the cache when it is complete. 3. When the variables change, a string `nextFetchPolicy` resets the policy to the initial value, so a new filter still gets a fresh request. 4. A function `nextFetchPolicy(currentFetchPolicy, context)` receives `context.reason`, either `after-fetch` or `variables-changed`, plus `initialFetchPolicy`. It decides both cases itself. Setting `nextFetchPolicy` in `defaultOptions.watchQuery` applies one rule to every watched query that does not set its own. ## Mistakes to avoid - Picking `no-cache` "to be safe" for a list, then wondering why a mutation's result never shows in it. - Believing `network-only` results are not cached. They are written, and other queries can read them. - Using `cache-first` for data the server changes on its own, with nothing to refresh it. Polling, `cache-and-network`, or the opt-in event refetching added in 4.2 cover that case.
- Does Apollo Client 4 refetch the issue list when the browser tab regains focus?Not by default. Since 4.2 it is opt-in: pass a `RefetchEventManager` with `windowFocusSource` and, if you like, `onlineSource` to the `ApolloClient` constructor. Active queries then refetch on those events, and a single `useQuery` can opt out with `refetchOn: false` or `refetchOn: { windowFocus: false }`.
- Which fetch policies can a mutation use in Apollo Client 4?Only two. `network-only` is the default and writes the mutation result into the cache, which is what lets returned entities update the UI. `no-cache` sends the mutation without writing its result, so nothing on screen changes unless you refetch or update the cache yourself. `cache-first`, `cache-and-network` and the others do not apply to mutations.
- What exactly does nextFetchPolicy change?`fetchPolicy` governs the first execution. After each fetch, Apollo switches the watched query's policy to `nextFetchPolicy`, so a query that opened with `network-only` or `cache-and-network` is served `cache-first` on later executions. On a variables change, a string `nextFetchPolicy` resets to the initial policy. A function receives `reason` (`after-fetch` or `variables-changed`) and chooses for itself.
saying these in an interview costs you the question
- cache-and-network sends a network request every time the cache changes.
- network-only results are never written to the cache.
- A no-cache list still updates when a mutation writes the same issue.
- The default fetchPolicy for useQuery is cache-and-network.
- nextFetchPolicy replaces fetchPolicy for the very first request.