You are viewing documentation for a preview version of this software.

Learn about previews.

Expiration management with the normalized cache


The normalized cache can be configured to store expiration information using a max-age. This is also sometimes referred to as TTL (Time To Live) or freshness.

Max-age can be configured by the server, by the client, or both.

Server-controlled max-age

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

Apollo Server can be configured to include the Cache-Control header in responses. See the caching documentation for more information.

The Expires HTTP header is not supported. Only Cache-Control is.

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:

Kotlin
1val apolloClient = ApolloClient.builder()
2  .serverUrl("https://example.com/graphql")
3  .storeExpirationDate(true)
4  .normalizedCache(
5    normalizedCacheFactory = /*...*/,
6    cacheResolver = CacheControlCacheResolver(),
7  )
8  .build()

Expiration dates are stored and when a field is resolved, the cache resolver will check if the field is stale. If so, it will return an error.

Client-controlled max-age

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 max age.

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

Kotlin
1val apolloClient = ApolloClient.builder()
2  .serverUrl("https://example.com/graphql")
3  .storeReceivedDate(true)
4  .normalizedCache(
5    normalizedCacheFactory = /*...*/,
6    cacheResolver = CacheControlCacheResolver(maxAgeProvider),
7  )
8  .build()

Expiration dates and received dates can be both stored to combine server-controlled and client-controlled expiration strategies.

The max 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:

Kotlin
1    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:

Kotlin
1cacheResolver = CacheControlCacheResolver(
2  SchemaCoordinatesMaxAgeProvider(
3    maxAges = mapOf(
4      "Query.cachedBook" to MaxAge.Duration(60.seconds),
5      "Query.reader" to MaxAge.Duration(40.seconds),
6      "Post" to MaxAge.Duration(4.minutes),
7      "Book.cachedTitle" to MaxAge.Duration(30.seconds),
8      "Reader.book" to MaxAge.Inherit,
9    ), 
10    defaultMaxAge = 1.hours,
11  )
12),

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 directives:

GraphQL
1# First import the directives
2extend schema @link(
3  url: "https://specs.apollo.dev/cache/v0.4",
4  import: ["@cacheControl", "@cacheControlField"]
5)
6
7# Then extend your types
8extend type Query @cacheControl(maxAge: 60)
9  @cacheControlField(name: "cachedBook", maxAge: 60)
10  @cacheControlField(name: "reader", maxAge: 40)
11
12extend type Post @cacheControl(maxAge: 240)
13
14extend type Book @cacheControlField(name: "cachedTitle", maxAge: 30)
15
16extend type Reader @cacheControlField(name: "book", inheritMaxAge: true)

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

Kotlin
1cacheResolver = CacheControlCacheResolver(
2  SchemaCoordinatesMaxAgeProvider(
3    maxAges = Cache.maxAges,
4    defaultMaxAge = 1.hours,
5  )
6)

Note: the cache() extension generated by the compiler plugin configures this automatically.

Feedback

Edit on GitHub

Ask Community