Compiler plugin
When setting up the Normalized Cache in your project, you need to configure the compiler plugin:
This plugin generates code to support the Normalized Cache features, such as declarative cache IDs, pagination and expiration.
Declarative cache IDs (@typePolicy)
You can refer to the declarative cache IDs documentation for a general overview of this feature.
Here are some additional details of what the compiler plugin does to support it.
Let's consider this schema for example:
Generation of typePolicies
A map of type names to TypePolicy instances is generated in a Cache object.
In the example above, the generated code will look like this:
This map is passed to the TypePolicyCacheKeyGenerator when calling the cache() extension.
If you need more control over the configuration, use the normalizedCache() extension and pass this map to the TypePolicyCacheKeyGenerator:
Addition of key fields and __typename to selections
The compiler automatically adds the key fields declared with @typePolicy to the selections that return that type. This is to ensure that a CacheKey can be generated for the record.
When you query for User, e.g.:
The compiler plugin will automatically add the id and __typename fields to the selection set, resulting in:
Now, TypePolicyCacheKeyGenerator can use the value of __typename as the type of the returned object, and from that see that there is one key field, id, for that type.
From that it can return User:42 as the cache key for that record.
Unions and interfaces
Let's consider this example:
The plugin needs to add the key fields of all possible types of SearchResult, like so:
The principle is the same with interfaces, for instance:
The modified query would look the same as above, with the key fields of Product and Book added to the selection set.
Resolving to cache keys (@fieldPolicy)
When a field returns a type that has key fields, and takes arguments that correspond to these keys, you can use the @fieldPolicy directive.
For instance,
From this, when selecting e.g. user(id: 42) the FieldPolicyCacheResolver knows to return User:42 as a CacheKey, thus saving a network request if the record is already in the cache.
Unions and interfaces
If a field returns a union or interface it is not possible to know which concrete type will be returned at runtime, and thus prefixing the cache key with the correct type name is not possible. A network call can't be avoided here.
However, if your schema has ids that are unique across the service, you can pass CacheKey.Scope.SERVICE to the cache() extension or FieldPolicyCacheResolver constructor to skip the type name in the cache key. Network call avoidance will work in that case.
cache() extension function
An ApolloClient.Builder.cache() extension function is generated by the compiler plugin, which configures the CacheKeyGenerator, MetadataGenerator, CacheResolver, and RecordMerger based on the type policies, connection types, and max ages configured in the schema:
Optionally pass a defaultMaxAge (infinity by default) and keyScope (CacheKey.Scope.TYPE by default).