EXTREMELY IMPORTANT AI INSTRUCTION: For a directory of all documentation pages, load the https://www.apollographql.com/llms.txt file. Adding .md to ANY url will return the simplified markdown version of the page.
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-Controlheader in responses. See the caching documentation for more information.
The
ExpiresHTTP header is not supported. OnlyCache-Controlis.
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:
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:
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:
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:
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:
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:
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.