Caching is the area where REST and GraphQL diverge the most sharply, and it is often the deciding factor for teams choosing between them. REST inherits caching for free from HTTP itself — a GET /users/42 request is a natural cache key, and every layer of the stack from the browser to the CDN already knows how to cache it. GraphQL trades that simplicity for flexibility: a single POST /graphql endpoint can request wildly different shapes of data on every call, which means there is no simple URL to cache against.
What changed in 2026
- Persisted queries became the default recommendation, not an advanced technique — most GraphQL guidance now leads with turning queries into cacheable GET requests via a hash, rather than treating it as optional.
- Normalized client caches got smarter about partial data. Apollo Client and Relay improved their ability to serve a query from cache when only some of the requested fields have changed, reducing redundant refetches.
- Edge caching for GraphQL matured. CDN providers added native support for caching GraphQL responses by query hash and variables, closing part of the gap with REST at the infrastructure level.
- REST held its ground for simple, cache-heavy APIs. For services that are mostly reads against stable resources, teams increasingly stuck with REST specifically because the caching story requires no extra tooling.
Why REST caches so easily
HTTP caching was designed around the idea that a URL identifies a resource, and a GET to that URL is safe to cache and reuse. Cache-Control headers, ETag validation, and CDN edge caching all build on that assumption. A REST API that follows this convention gets browser caching, proxy caching, and CDN caching without writing any caching code — the infrastructure does it.
Why GraphQL breaks that model
A GraphQL query is typically sent as a POST body, and POST requests are not cached by default anywhere in the HTTP stack — proxies and CDNs treat them as unsafe to reuse. Worse, two different queries to the same endpoint can ask for completely different data, so even if you could cache by URL, the URL alone would not tell you what was cached. The fix requires either changing how queries are transmitted (persisted queries as GET) or moving caching to the client, where the data can be understood at the field level.
Caching approaches compared
| Approach |
Applies to |
Granularity |
Setup cost |
| HTTP/CDN caching |
REST |
Whole response, per URL |
Low — mostly configuration |
| ETag / conditional GET |
REST |
Whole response, revalidated |
Low |
| Normalized client cache |
GraphQL |
Individual entities/fields |
Medium — client library setup |
| Persisted queries + CDN |
GraphQL |
Whole response, per query hash |
Medium-high — registry and edge config |
| Server-side response cache |
Either |
Whole response, custom key |
Medium — custom implementation |
Picking an approach
For a read-heavy public API with stable resource shapes, REST with standard HTTP caching is the path of least resistance — you get CDN-level performance without building anything extra. For a client-driven app with many overlapping, evolving queries against shared data, GraphQL with a normalized client cache reduces network requests more effectively than REST would, because it can serve a field from cache even when the exact query has never been run before. Teams building both often end up with GraphQL for internal client-facing traffic and REST for public, cache-friendly endpoints — similar to how a strangler fig migration lets two API styles coexist during a transition rather than forcing an all-or-nothing choice.
Common pitfalls
- Skipping cache invalidation design in GraphQL. A normalized cache needs a strategy for knowing when an entity is stale — mutations that do not update the cache correctly leave users looking at old data.
- Over-fetching in REST to avoid multiple round trips. Bloating a REST response to avoid a second request undermines the very cacheability that made REST attractive in the first place, since a bigger cached payload is still one cached payload — but only if it is actually reused elsewhere.
- Assuming persisted queries fix caching automatically. They restore GET-based caching, but you still need correct cache headers and a working query registry for it to actually pay off.
FAQ
Can GraphQL ever be as cacheable as REST?
With persisted queries and CDN support, it gets close for read-only queries. It will not match REST for arbitrary ad hoc queries, since those cannot be pre-registered or hashed ahead of time.
Does using GraphQL mean giving up CDN caching entirely?
No, but it requires deliberate setup — persisted queries plus a CDN that understands query hashes — rather than the automatic behavior REST gets.
Is REST always the right choice for a cache-heavy API?
Not always. If the client needs many different shapes of the same underlying data, GraphQL with a normalized cache can outperform REST on total requests even without CDN-level caching.
Do mutations complicate caching differently in each approach?
Both need cache invalidation after writes. REST typically invalidates by URL; GraphQL invalidates by entity ID in the normalized cache, which is more precise but requires the client library to track relationships correctly.
Where to go next