Apollo 7: Type Policies
easy⏱ 5 mincourseapollo
keyFields, merge, and read functions
Type policies are configured in InMemoryCache and let you customize three aspects of cache behavior per type:
- keyFields: Define which fields make an object unique. Default is
['id'] or ['_id']. For objects without an id, use keyFields: ['slug'] or keyFields: false (singleton).
- merge: Custom logic for merging incoming data with existing cached data. Essential for pagination — without it, fetching page 2 overwrites page 1.
- read: Transform data when it's read from cache. Great for computed fields, formatting, or default values.
const cache = new InMemoryCache({
typePolicies: {
Product: {
keyFields: ['sku'],
},
Query: {
fields: {
posts: {
keyArgs: ['category'],
merge(existing = [], incoming) {
return [...existing, ...incoming];
},
},
},
},
},
});
Configure pagination merge
Create an InMemoryCache with type policies. Set Product to use keyFields: ['sku']. Configure Query.fields.products with a merge function that appends incoming items to existing ones (for infinite scroll). Add keyArgs: ['category'] so each category has its own cache entry. Finally, add a read function on User.fullName that concatenates firstName and lastName.
Computed fields via read
The read function lets you create computed fields that don't exist in your schema. Access other fields on the same object via readField: read(_, { readField }) { return readField('firstName') + ' ' + readField('lastName'); }. Components can query fullName as if it were a real field. Combine with @client directive for local-only computed properties.
User: {
fields: {
fullName: {
read(_, { readField }) {
const first = readField('firstName');
const last = readField('lastName');
return `${first} ${last}`;
},
},
},
}
Normalization control
By default, Apollo normalizes using __typename:id. When your data has different identifiers (sku, slug, email), or no identifier at all (singletons like currentUser), type policies give you full control. Use keyFields: false to disable normalization for a type (it becomes embedded in its parent). Use keyFields: ['isbn', 'edition'] for composite keys.