Join us from October 8-10 in New York City to learn the latest tips, trends, and news about GraphQL federation and API platform engineering.Join us for GraphQL Summit 2024 in NYC
Docs
Start for Free

Declarative cache IDs in Apollo Kotlin


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

When specifying cache IDs, it's recommended that you do so declaratively, using the methods described in this article:

extend type Book @typePolicy(keyFields: "id")

For advanced use cases, you can also define cache IDs programmatically.

How they work

With declarative cache IDs, the codegen process adds ID automatically and generates type-safe code that can return a valid cache ID for any object.

To do this, Apollo Kotlin extends your backend schema. The spec supports object extensions using the extend type keywords. We can use this together with the @typePolicy and @fieldPolicy , along with an extra .graphqls file named extra.graphqls.

Note that you'll need to make sure that the extra.graphqls file is included in your schema configuration.

You can obtain a given object type's cache ID from one of two sources:

SourceDirectiveDescription
From a response object's fields (e.g., Book.id)@typePolicyThis happens after a network request and is essential to merging a query result with existing cached data. This is the most common case.
From a field's arguments (e.g., author(id: "au456"))@fieldPolicyThis happens before a network request and enables you to avoid a network round trip if all requested data is in the cache already. This is an optional optimization that can avoid some cache misses.

@typePolicy

The @typePolicy enables you to specify an object's cache ID from key fields of the object returned by your . Most commonly, you can use an object's id as its key field.

For example, let's say our app's schema.graphqls file includes the following definition:

schema.graphqls
type Book {
id: String!
author: Author!
title: String!
}

We can add the following definition to an extra.graphqls file in the same directory as our schema:

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

Apollo Kotlin now knows to use the id field of a Book type to generate its cache ID. A cache record may now look like

"Book:bk123": {"id": "bk123", "title": "Les guerriers du silence", "author": "ApolloCacheReference{favoriteBook.author}"}

You can specify multiple key fields for an object if they're all required to uniquely identify a particular cache entry:

extra.graphqls
extend type Author @typePolicy(keyFields: "firstName lastName")

In this case, the cache ID for an Author object includes the values of both its firstName and lastName fields:

"Author:PierreBordage": {"id": "au456", "firstName": "Pierre", "lastName": "Bordage"}

All of an object type's key fields must return a scalar type.

Note that the key fields specified this way will automatically be added during code generation to selections on the object type, since they are always needed to identify the object in the cache. This means you don't need to include them in your queries.

Adding __typename to your operations

In addition to the key fields, the declarative cache requires the __typename of each object by default. These typenames are not included by default as they make larger for non-cache users. To avoid cache misses, add __typename to every selection using addTypename:

apollo {
service("service") {
addTypename.set("always")
}
}

@fieldPolicy

The @fieldPolicy directive enables you to specify an object's cache ID from the values of key arguments you provide to a particular field. This enables you to identify an object in your cache before sending a network request, potentially enabling you to skip the request entirely.

For example, let's say our app's schema.graphqls file includes the following definition:

schema.graphqls
type Query {
book(id: String!): Book
}

We happen to know that this query returns whichever Book object has an id field that matches the required . Therefore, we can make the id argument a key argument for this field.

We can add the following definition to an extra.graphqls file in the same directory as our schema:

extra.graphqls
extend type Query @fieldPolicy(forField: "book", keyArgs: "id")

Apollo Kotlin now knows to check the cache for a Book object with the provided id before sending a network request for Query.book.

Note that even though the @fieldPolicy directive corresponds to a single field, you apply the directive to the type definition (Query in this case). This is because GraphQL doesn't allow extending a single field. You specify which field the directive corresponds to with the forField argument.

You can specify multiple key for a field if they're all required to uniquely identify a particular cache entry:

extra.graphqls
extend type Query @fieldPolicy(forField: "author", keyArgs: "firstName lastName")

In this case, the cache ID for an Author object includes the values of both its firstName and lastName fields, which are both provided as arguments to the Query.author field.

If multiple fields of an object type have key arguments, you can apply multiple @fieldPolicy directives to that type.

Previous
Normalized caches
Next
Programmatic cache IDs
Rate articleRateEdit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company