Build the mental model
Because every GraphQL query has a different shape, REST-style URL-based HTTP caching does not apply directly — field-level `@cacheControl` directives or a response-caching plugin set per-field TTLs instead. Since a client can send a deeply nested or recursive query (like `author { tutorials { author { tutorials { ... } } } }`), server-level validation rules should enforce a depth limit and a complexity score (field cost weighting) to prevent resource-exhaustion attacks. Persisted queries send only a hash id instead of the full query text over the network, cutting bandwidth and parsing cost.
Connect it to a real scenario
Mark the Tutorial Platform's `tutorials` query field with `@cacheControl(maxAge: 60)` to cache public catalog data for 60 seconds. Use a `depthLimit(6)` validation rule to block recursive author-tutorials-author attack queries, and `createComplexityLimitRule(1000)` to cap the field-count-weighted total.
Try the working example
import depthLimit from 'graphql-depth-limit';
import { createComplexityLimitRule } from 'graphql-validation-complexity';
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [depthLimit(6), createComplexityLimitRule(1000)],
});You can wire up depth/complexity limiting rules and explain a caching strategy.5-minute try-it
Write a 3-level nested query attack (`tutorials { comments { author { tutorials } } }`) and explain how `depthLimit` would block it.
One important caution
Applying `@cacheControl` to mutation fields can return stale or incorrect write results to the client — reserve caching for read-only query fields.
Apollo Server — Caching — GraphQL