GraphQL in Production · Performance and Caching
Client Caching and State
Normalized caches, cache policies and updates after mutations.
GraphQL clients (Apollo Client, urql with Graphcache, Relay) keep a normalized cache keyed by __typename + id. After mutations, objects returned in payloads update automatically; list membership changes (add/remove) need explicit cache updates or refetching. Choose fetch policies per query: cache-first for stable data, cache-and-network for freshness, network-only for sensitive data. On mobile, persist the cache for offline startup.
Common fetch policies
| Fetch policy | Behavior | Use for |
|---|---|---|
| cache-first | Cache if present, else network | Stable reference data |
| cache-and-network | Return cache, then refresh | Feeds, dashboards |
| network-only | Always network, writes cache | Must-be-fresh screens |
| no-cache | Network, doesn't store | Sensitive data |
// Apollo Client: update a list after a mutation
const [addItem] = useMutation(ADD_CART_ITEM, {
update(cache, { data }) {
cache.modify({
id: cache.identify({ __typename: "Cart", id: data.addCartItem.cart.id }),
fields: { items: (existing = []) => [...existing, cache.writeFragment({ data: data.addCartItem.item, fragment: CART_ITEM })] },
});
},
optimisticResponse: { addCartItem: { __typename: "AddCartItemPayload", cart: { __typename: "Cart", id: cartId }, item: { __typename: "CartItem", id: "temp", quantity: 1 } } },
});
Common pitfalls
- Missing
idfields in selections (objects can't be normalized). - Forgetting list updates after create/delete.
- Caching sensitive data persistently on devices.
Key points
- Normalized by __typename + id
- Payloads update caches; lists need help
- Pick fetch policies per screen
Further reading: Apollo Client: Caching
Practice questions
Why should every selection include the id field?
Answer
Normalized caches identify objects by type and id; without it, objects can't be merged or updated after mutations.
What does an optimistic response do?
Answer
It writes the expected mutation result to the cache immediately so the UI updates instantly, then replaces it with the real server response (or rolls back on error).
Open in the app to mark lessons complete, take notes and track your progress.