Docs
Launch GraphOS Studio

Server-side caching


Using cache control with requires at least v3.0.2 of in your s and gateway. Please note that 's cache hints API has evolved as of v3, so be sure to review the updated caching documentation.

Using cache hints with subgraphs

To set static cache hints with -based s, the @cacheControl and CacheControlScope enum definitions must be included in the :

enum CacheControlScope {
PUBLIC
PRIVATE
}
directive @cacheControl(
maxAge: Int
scope: CacheControlScope
inheritMaxAge: Boolean
) on FIELD_DEFINITION | OBJECT | INTERFACE | UNION

The calculates and sets the cache hint for the response that it sends to the gateway, and the gateway then calculates the cache hint for the overall response. This hint is based on the most restrictive settings among all of the responses received from the subgraphs involved in plan execution.

Cache hints can also be set dynamically in subgraph resolvers.

Setting entity cache hints

s define an _entities root on the Query type, so all plans that require entity resolution have a maxAge of 0 set by default. To override this default behavior, you can add a @cacheControl to an entity's definition:

type Book @key(fields: "isbn") @cacheControl(maxAge: 30) {
isbn: String!
title: String
}

When the _entities is resolved it checks the applicable concrete type for a cache hint (e.g. the Book type in the example above) and applies that hint instead.

To set cache hints dynamically, the cacheControl object and its methods are also available in the info parameter of the __resolveReference .

Overriding subgraph cache hints in the gateway

If a does not specify a max-age, the gateway will assume its response (and in turn, the overall response) cannot be cached. To override this behavior, you can set the cache-control header in the didReceiveResponse method of a RemoteGraphQLDataSource.

Additionally, if the gateway should ignore cache-control response headers from s that will affect the 's cache policy, then you can set the honorSubgraphCacheControlHeader property of a RemoteGraphQLDataSource to false (this value is true by default).

The effect of setting honorSubgraphCacheControlHeader to false is to have no impact on the cacheability of the response in either direction. In other words, this property won’t determine whether the response can be cached, but it does exclude a 's cache-control header from consideration in the gateway's calculation. If all s are excluded from consideration when calculating the overall cache-control header, the response sent to the client will not be cached.

There are detailed examples below that illustrate the resulting behavior when overriding this header for a .

Example maxAge calculations

Multi-subgraph entities

Consider the following s:

Astronauts Subgraph
type Astronaut @key(fields: "id") @cacheControl(maxAge: 20) {
id: ID!
name: String
}
type Query {
astronaut(id: ID!): Astronaut
astronauts: [Astronaut]
}
Missions Subgraph
type Mission {
id: ID!
crew: [Astronaut]
designation: String!
startDate: String
endDate: String
}
type Astronaut @key(fields: "id") {
id: ID!
missions: [Mission]
}
type Query {
mission(id: ID!): Mission @cacheControl(maxAge: 10)
missions: [Mission]
}

For the following :

query GetMissionWithCrew {
mission(id: 1) {
designation
crew {
name
}
}
}

The response will be considered not cacheable.

The cache-control header in the response from the astronauts will be max-age=20, public based on the cache hint applied to the Astronaut type, which will be taken into consideration when this responds to the _entities from the gateway.

The missions , however, does not include a cache-control header in its response to the gateway, thus indicating that it is not cacheable. Consequently, the entire response sent to the client is not cacheable. At first glance, this behavior might seem unexpected because the @cacheControl(maxAge: 10) is applied to the mission root . But upon closer inspection, we see that this field returns the Mission type which contains a non- Astronaut and this type will have a default maxAge of 0 applied.

The missions isn't aware of the cache hint set on the Astronaut type in the astronauts , so there are two ways to address this. The first option is to apply the @cacheControl to the Astronaut entity in the missions , with the inheritMaxAge set to true:

Missions Subgraph
type Mission {
id: ID!
crew: [Astronaut] @cacheControl(inheritMaxAge: true)
designation: String!
startDate: String
endDate: String
}

Applying this will ensure that the Astronaut type inherits the maxAge set for its parent (in this example, that's the mission ).

The second option is to set the @cacheControl for the Astronaut entity with the appropriate maxAge value instead:

Missions Subgraph
type Astronaut @key(fields: "id") @cacheControl(maxAge: 20) {
id: ID!
missions: [Mission]
}

Setting the maxAge property on the Astronaut type in the missions raises an important consideration about whether every should explicitly set these values for any entity types that they include. In most cases, it's likely preferable to apply @cacheControl(inheritMaxAge: true) wherever an entity type is used as a 's return type to avoid ambiguity. Depending on your requirements, you might want to disallow this entirely using custom linting for your s to ensure that multiple subgraphs do not set the maxAge or scope s when applying the @cacheControl .

Gateway override

For these s:

Astronauts Subgraph
type Astronaut @key(fields: "id") @cacheControl(maxAge: 20) {
id: ID!
name: String
}
type Query {
astronaut(id: ID!): Astronaut
astronauts: [Astronaut]
}
Missions Subgraph
type Mission {
id: ID!
crew: [Astronaut] @cacheControl(inheritMaxAge: true)
designation: String!
startDate: String
endDate: String
}
type Astronaut @key(fields: "id") {
id: ID!
missions: [Mission]
}
type Query {
mission(id: ID!): Mission @cacheControl(maxAge: 10)
missions: [Mission]
}

And this :

query GetMissionWithCrew {
mission(id: 1) {
designation
crew {
name
}
}
}

The response will have a cache-control header value of max-age=10, public, as expected.

However, if you wanted to disregard cache-control headers supplied by the missions , you could do so by setting honorSubgraphCacheControlHeader in the RemoteGraphQLDataSource options to false for that :

const gateway = new ApolloGateway({
// ...
buildService({ name, url }) {
return new RemoteGraphQLDataSource({
url,
honorSubgraphCacheControlHeader: name === "missions" ? false : true;
});
}
});

The response will now have a cache-control header value of max-age=20, public because only the cache hint from the astronauts will be considered in the gateway's calculation of the overall header.

Alternatively, we could instead override the max-age=10, public header from the missions response and set it to a completely different value as follows:

const gateway = new ApolloGateway({
// ...
buildService({ name, url }) {
return new RemoteGraphQLDataSource({
url,
didReceiveResponse({ response }) {
if (name === "missions") {
response.http.headers.set(
"cache-control",
"max-age=5, public"
);
}
return response;
}
});
}
});

The overall response will now have a cache-control header value of max-age=5, public because the missions 's overridden header is more restrictive than the max-age=20, public header that was supplied by the astronauts .

Previous
Opt-in error reporting
Next
Monitoring
Edit on GitHubEditForumsDiscord