Concept
The beginner framing: Apollo Client already covered how the cache stores data (normalized entities, shared references). This topic covers the layer above that: when a query should trust what's cached versus go to the network anyway, plus an entirely separate kind of caching, server/CDN-level, that Apollo Client's cache has no involvement in at all.
fetchPolicy: deciding when to trust the cache
useQuery(GET_USER, { fetchPolicy: "cache-first" }); // DEFAULT
useQuery(GET_USER, { fetchPolicy: "network-only" });
useQuery(GET_USER, { fetchPolicy: "cache-only" });
useQuery(GET_USER, { fetchPolicy: "no-cache" });
useQuery(GET_USER, { fetchPolicy: "cache-and-network" });Confirmed by testing the underlying mechanism directly: cache.readQuery() returns null, not an error, just null, when nothing has been written for that exact query yet. This null-on-miss behavior is precisely what fetchPolicy: "cache-first" (the default) checks: if readQuery comes back with data, use it and skip the network entirely; if it comes back null, fall through to a real network request. The other policies change this decision explicitly: network-only always fetches (but still writes the result into the cache afterward, so normalization still applies), cache-only never fetches at all (returning null/an error if nothing's cached), no-cache fetches and returns data without writing to the cache or normalizing it, and cache-and-network returns cached data immediately if available and fires a network request anyway, updating the UI again once that resolves.
Server-side and CDN caching: an entirely separate layer
type Query {
popularPosts: [Post!]! @cacheControl(maxAge: 60)
}This is a completely different caching concern from anything Apollo Client's normalized cache touches, it's about the server telling infrastructure in front of it (a CDN, a reverse proxy, or Apollo Server's own response cache) how long a given field's result can be reused across different clients entirely, not just within one client's session. @cacheControl(maxAge: 60) on a schema field declares that field's result can be cached for 60 seconds, genuinely useful for data like "popular posts" that doesn't need to be perfectly real-time for every single request.
Persisted queries: confirmed built into Apollo Server's core
POST /graphql
{ "extensions": { "persistedQuery": { "sha256Hash": "abc123..." } } }Confirmed present in Apollo Server 5's core package internals (not a separate plugin dependency): Automatic Persisted Queries let a client send a query's hash instead of its full text on repeat requests, the server recognizes the hash (having seen the full query text on a prior request) and executes it without needing the text resent. This shrinks request payload size, and, since GraphQL requests are normally POSTs (not cacheable by standard HTTP caching, which is GET-oriented), persisted queries with a hash-based GET fallback make GraphQL responses genuinely cacheable by CDNs and browsers the way a REST GET endpoint's response would be.
Try It
Predict the outcome before checking the solution.