Custom cache keys


When working with a normalized cache, it is recommended that you specify a cache ID for each object type in your schema. If you don't, objects are assigned a default cache ID, but that ID can lead to undesirable duplication of data.

The normalized cache computes a cache key for each object that is stored in the cache. With Apollo iOS, you can customize the computation of cache keys to improve the performance and capabilities of your cache.

To learn more, read about how the cache normalizes objects by cache key.

For most use cases, you can configure cache keys declaratively using the methods described on this page. For advanced use cases not supported by declarative cache keys, you can configure cache keys programmatically.

Schema extensions

Declarative cache keys work by adding directives to your backend schema types to indicate how cache keys should be resolved for those types. To do this, you can extend your backend schema by creating a schema extension file with the file extension .graphqls. You can add any number of schema extension files to your project.

note
Make sure you include your schema extension files in the schemaSearchPaths of your code generation configuration's FileInput.

The @typePolicy directive

The @typePolicy directive lets you specify an object's cache ID using key fields of the object returned by your GraphQL server. You can apply @typePolicy to both concrete object types and interface types in your schema.

To declare a type policy, extend the type with the @typePolicy directive in a .graphqls schema extension file.

Key fields

The @typePolicy directive has a single keyFields argument, which takes a string indicating the fields used to determine a type's cache key.

GraphQL
typePolicies.graphqls
extend type Book @typePolicy(keyFields: "id")

If you add the above schema extension file , Apollo iOS resolves the cache key for all objects with a __typename of "Book" by using the value of their id field. The concrete type of the object is always prepended to the cache key. This means that a Book object with an id of 456 will resolve to have a cache key of Book:456.

note
All of a type's @typePolicy key fields must return a scalar type. To use non-scalar fields in cache keys, use programmatic cache key configuration.

Multiple key fields

You can specify multiple key fields for an object if they are all required to uniquely identify a particular cache entry. Separate multiple key fields with a single space when declaring them:

GraphQL
typePolicies.graphqls
1extend type Author @typePolicy(keyFields: "firstName lastName")

With this extension, the resolved cache key includes all declared key fields and separates them by the + character. In this case, an Author object with a firstName of "Ray" and a last name of "Bradbury" would resolve to have a cache key of Author:Ray+Bradbury.

Type policies on interface types

You can also use @typePolicy on interface types. Doing so specifies a default set of key fields for all types that implement that interface.

GraphQL
extra.graphqls
1extend interface Node @typePolicy(keyFields: "id")
note
Overriding type policies is not currently supported. If an object type has a @typePolicy, it must match any type policies of any interfaces the object type implements.

Caveats and limitations

Declarative cache key resolution has a few limitations you should be aware of while implementing your @typePolicy directives:

Conflicting Type Policies

If any type implements two interfaces with conflicting type policies, Apollo iOS throws a validation error when executing code generation.

Cache Inconsistencies

When an object is returned for a field of an interface type where the interface has a @typePolicy, Apollo iOS will first attempt to find a @typePolicy for the concrete type by using the __typename field of the returned object. If the type does not have an @typePolicy of its own, the interface's @typePolicy will be applied.

If the same object is returned for multiple fields of different interface types with conflicting type policies, it is possible that the same object is resolved with two different type policies, leading to cache inconsistencies.

In most circumstances, validation that is run during code generation prevents this from occurring. However, this may still occur if changes are made to the schema after code generation runs or after your application is published.

This is possible if:

A) A type is added to your schema after code generation is executed and that type implements two different interfaces with different type policies

B) Implementation of an interface is added to an existing type which already implements an interface with a different type policy

Feedback

Edit on GitHub

Forums