How does the Apollo Client cache normalize data?
Learn how Apollo Client normalizes GraphQL data into a flat cache keyed by __typename and id, using references and typePolicies for automatic UI consistency.
Expected Interview Answer
Apollo Client normalizes data by flattening every query result into a single in-memory table, keyed by a stable cache ID (usually __typename plus the object's id), so each object is stored exactly once regardless of how many queries returned it.
When a response arrives, Apollo splits nested objects into individual entries, assigns each a cache key like 'User:42', and replaces the object with a reference to that key inside its parent. Because every query points at the same normalized record, updating one field automatically updates every part of the UI that reads it. You customize the key with keyFields in typePolicies, and objects lacking an id are stored inline unless you tell Apollo how to identify them.
- Each entity stored once, no duplication
- Automatic UI consistency across queries
- Cheaper updates: change one record, all readers refresh
- Enables cache reads without refetching
- Configurable identity via keyFields typePolicies
AI Mentor Explanation
Think of a scorer who never rewrites a batter's full profile on each delivery; instead a central player registry holds each cricketer once under a unique cap number, and every ball entry just cites that number. Update a player's team once and all thousands of ball records instantly reflect it — exactly how Apollo stores each entity once and references it everywhere.
Step-by-Step Explanation
Step 1
Receive the response
Apollo takes the nested JSON returned by a GraphQL query as the raw input to normalize.
Step 2
Compute a cache ID
For each object it builds a key like 'User:42' from __typename plus id (or custom keyFields).
Step 3
Flatten into a table
Every keyed object is stored as its own flat entry in the normalized cache map.
Step 4
Replace with references
Inside parent objects, the nested object is swapped for a { __ref } pointer to its cache key.
Step 5
Read back via references
When queries read the cache, Apollo follows references to reassemble the shaped result.
What Interviewer Expects
- Knowledge of the __typename + id cache key convention
- Understanding of references (__ref) replacing nested objects
- How keyFields / typePolicies customize identity
- Why normalization yields automatic UI consistency
- Awareness of objects without ids falling back to inline storage
Common Mistakes
- Thinking Apollo stores query results verbatim as a tree
- Forgetting to request id, breaking automatic normalization
- Assuming normalization works without __typename
- Confusing normalized cache with a simple response-to-query map
- Not configuring keyFields for entities keyed differently than id
Best Answer (HR Friendly)
“Apollo Client stores data efficiently by keeping just one copy of each object in a shared table, identified by a unique key. Different parts of the app point to that single copy, so when data changes in one place, everything using it updates automatically.”
Code Example
import { InMemoryCache } from '@apollo/client'
const cache = new InMemoryCache({
typePolicies: {
User: {
// Default is __typename + id, e.g. 'User:42'
keyFields: ['id'],
},
Book: {
// Compose a key from multiple fields
keyFields: ['isbn', 'edition'],
},
Setting: {
// Singleton with no id: store as a single record
keyFields: [],
},
},
})
// A query result becomes references internally:
// { user: { __ref: 'User:42' } }
// with the actual fields stored under cache['User:42']Follow-up Questions
- What happens to an object in the cache if it has no id field?
- How do you update a normalized entity manually with cache.modify?
- How does dataIdFromObject differ from keyFields in newer Apollo versions?
- Why can a mutation automatically update the UI without a refetch?
MCQ Practice
1. What is the default cache key Apollo uses for a normalized object?
By default Apollo builds keys like 'User:42' from __typename plus the object's id.
2. Inside a parent object in the normalized cache, a nested entity is stored as:
Apollo replaces the nested object with a reference pointer so each entity is stored only once.
3. How do you tell Apollo to key an entity by a field other than id?
keyFields in typePolicies overrides the default identity, e.g. ['isbn'].
Flash Cards
What is the default Apollo cache key? — __typename + id, e.g. 'User:42'.
How are nested entities represented? — As { __ref: 'Type:id' } references pointing to a flat entry.
How do you customize identity? — keyFields in typePolicies (e.g. ['isbn'] or [] for singletons).
Why does normalization keep the UI consistent? — Every query references the same record, so one update refreshes all readers.
What breaks automatic normalization? — Omitting id (or __typename), forcing inline storage under the parent.