apollo-kotlin-normalized-cache-incubating Help

Cache control

The cache control feature takes the freshness of fields into consideration when accessing the cache. This is also sometimes referred to as TTL (Time To Live) or expiration.

Freshness can be configured by the server, by the client, or both.

Server-controlled

When receiving a response from the server, the Cache-Control HTTP header can be used to determine the expiration date of the fields in the response.

The cache can be configured to store the expiration date of the received fields in the corresponding records. To do so, call .storeExpirationDate(true), and set your client's cache resolver to CacheControlCacheResolver:

val apolloClient = ApolloClient.builder() .serverUrl("https://example.com/graphql") .storeExpirationDate(true) .normalizedCache( normalizedCacheFactory = /*...*/, cacheResolver = CacheControlCacheResolver(), ) .build()

Expiration dates will be stored and when a field is resolved, the cache resolver will check if the field is stale. If so, it will throw a CacheMissException.

Client-controlled

When storing fields, the cache can also store their received date. This date can then be compared to the current date when resolving a field to determine if its age is above its maximum age.

To store the received date of fields, call .storeReceivedDate(true), and set your client's cache resolver to CacheControlCacheResolver:

val apolloClient = ApolloClient.builder() .serverUrl("https://example.com/graphql") .storeReceivedDate(true) .normalizedCache( normalizedCacheFactory = /*...*/, cacheResolver = CacheControlCacheResolver(maxAgeProvider), ) .build()

The maximum age of fields can be configured either programmatically, or declaratively in the schema. This is done by passing a MaxAgeProvider to the CacheControlCacheResolver.

Global max age

To set a global maximum age for all fields, pass a GlobalMaxAgeProvider to the CacheControlCacheResolver:

cacheResolver = CacheControlCacheResolver(GlobalMaxAgeProvider(1.hours)),

Max age per type and field

Programmatically

Use a SchemaCoordinatesMaxAgeProvider to specify a max age per type and/or field:

cacheResolver = CacheControlCacheResolver( SchemaCoordinatesMaxAgeProvider( maxAges = mapOf( "Query.cachedBook" to MaxAge.Duration(60.seconds), "Query.reader" to MaxAge.Duration(40.seconds), "Post" to MaxAge.Duration(4.minutes), "Book.cachedTitle" to MaxAge.Duration(30.seconds), "Reader.book" to MaxAge.Inherit, ), defaultMaxAge = 1.hours, ) ),

Note that this provider replicates the behavior of Apollo Server's @cacheControl directive when it comes to defaults and the meaning of Inherit.

Declaratively

To declare the maximum age of types and fields in the schema, use the @cacheControl and @cacheControlField directive:

# First import the directives extend schema @link( url: "https://specs.apollo.dev/cache/v0.1", import: ["@cacheControl", "@cacheControlField"] ) # Then extend your types extend type Query @cacheControl(maxAge: 60) @cacheControlField(name: "cachedBook", maxAge: 60) @cacheControlField(name: "reader", maxAge: 40) extend type Post @cacheControl(maxAge: 240) extend type Book @cacheControlField(name: "cachedTitle", maxAge: 30) extend type Reader @cacheControlField(name: "book", inheritMaxAge: true)

Then configure the Cache compiler plugin in your build.gradle.kts:

apollo { service("service") { packageName.set(/*...*/) plugin("com.apollographql.cache:normalized-cache-apollo-compiler-plugin:0.0.4") { argument("packageName", packageName.get()) } } }

This will generate a map in yourpackage.cache.Cache.maxAges, that you can pass to the SchemaCoordinatesMaxAgeProvider:

cacheResolver = CacheControlCacheResolver( SchemaCoordinatesMaxAgeProvider( maxAges = Cache.maxAges, defaultMaxAge = 1.hours, ) ),

Maximum staleness

If stale fields are acceptable up to a certain value, you can set a maximum staleness duration. This duration is the maximum time that a stale field will be resolved without resulting in a cache miss. To set this duration, call .maxStale(Duration) either globally on your client, or per operation:

val response = client.query(MyQuery()) .fetchPolicy(FetchPolicy.CacheOnly) .maxStale(1.hours) .execute()

isStale

With maxStale, it is possible to get data from the cache even if it is stale. To know if the response contains stale fields, you can check CacheInfo.isStale:

if (response.cacheInfo?.isStale == true) { // The response contains at least one stale field }
Last modified: 29 October 2024