Cache Rules
The Stellate Edge Cache gives you fine-grained per-type or even per-field control over your cache behavior for every single query that is sent through your service with cache rules.
Basics
A basic rule to cache all queries that contain any User could look like this:
import { Config } from 'stellate'
const config: Config = {
config: {
rules: [
{
types: ['User'],
maxAge: 900,
swr: 900,
scope: 'AUTHENTICATED',
description: 'Cache Users',
},
],
},
}
export default config
A rule applies to all queries that contain the rule’s types or fields. If multiple rules apply to the same query, Stellate will use the minimum maxAge, the minimum swr
, and combine all scopes to cache the query result.
Introspection Query
Caching the introspection query is a special case, which is not handled via regular cache rules. Instead, you can enable caching by setting cacheIntrospection
to true
in your service configuration. By default responses to the introspection query are cached with a maxAge
of 63 minutes, as well as an swr
of 1 day. Those are currently not configurable via the configuration file.
If your service has ignoreOriginCacheControl
turned off, Stellate will respect the Cache-Control
headers present on the response to the introspection query and cache the response accordingly.
To invalidate cached introspection results, please use the CLI to push a new schema version.
import { Config } from 'stellate'
const config: Config = {
config: {
cacheIntrospection: true,
},
}
export default config
An extra origin cache rule
Cache-Control
headers will always be ignored if you have Partial Query
Caching enabled. For PQC to properly work, Cache-Control
headers set by your
origin can’t be taken into account.
When turning off ignoreOriginCacheControl
explicitly in your config we will treat the cache-control
header returned by your origin
as an additional cache-rule.
import { Config } from 'stellate'
const config: Config = {
config: {
ignoreOriginCacheControl: false,
rules: [
{
types: ['Post'],
maxAge: 900,
swr: 900,
description: 'Cache Posts publicly',
},
{
types: ['User'],
maxAge: 450,
swr: 1000,
scope: 'AUTHENTICATED',
description: 'Cache User data privately',
},
],
},
}
export default config
This means that after we calculate the max-age
and stale-while-revalidate
based on your rules we will compare them
to the values we get from your cache-control
and take the lowest of the two. Note that s-maxage
will take presedence
over max-age
when it comes to CDN-caching.
We take the rules of a shared cache in account,
this means that when we see an authorization
header we require the mention of public
, s-maxage
or must-revalidate
to take
the origin cache-control in account.
When we see any of the uncacheable directives like no-cache
, no-store
or private
we will treat this as a cache-pass.
Configuration
types
(required)
The rule will apply its cache configuration to any query that contains the types or fields mentioned in the types property.
You can target both types as well as specific fields of types:
import { Config } from 'stellate'
const config: Config = {
config: {
rules: [
{
types: {
// This rule will apply to any query that contains any User
User: true,
// This rule will apply to any query that contains Post.comments
Post: ['comments'],
// This rule will apply to any "{ drafts }" query
// (assuming the "Query" type is the root schema query type)
Query: ['drafts'],
},
},
],
},
}
export default config
maxAge
(seconds, optional)
The maxAge parameter configures how long query results that match the rule types should be cached, in seconds. If multiple rules apply to the same query, the minimum maxAge will be used for the entire query result.
Setting maxAge: 0
is a way of preventing any types and fields that match the
rule from ever be cached. However, we recommend using the
nonCacheable
config property for that purpose
instead.
swr
(stale-while-revalidate , seconds, optional)
The “swr” (stale-while-revalidate) property configures how long stale query results that match the rule types should be served while fresh data is already being fetched in the background.
For example, if you set the maxAge of a query to 60s and swr to 300s, depending on when new requests come in the following actions are taken:
- Within the first 60 seconds, the cached result will be returned from our edge locations.
- From 60s to 360s we will pass on the first request to your backend service, but continue to serve the cached (but stale) results for other identical requests. As soon as we get an updated response from your backend service we will update the cached data.
- Any requests coming in later than 360s will get passed on to your backend service. The first response will re-populate the cache.
We would recommend always configuring stale-while-revalidate
, as well as keeping the swr
limits fairly high. Your users will get a very quick response to their request, while we load potentially updated data in the background and update the cache.
If you want to read more about this topic, please see Keeping things fresh with stale-while-revalidate by Jeff Posnick.
scope
(string, optional)
Which scope to apply to query results that match the rule types. See the documentation on Scopes for more information on setting up scopes.
description
(string, optional)
Description of the purpose of the rule in plain English.
FAQ
How do I cache every GraphQL query?
To apply a rule to everything, target the root Query
type. This might look like the following example, if you did not rename that type.
Since the Query type is included in every single query, this rule will apply
to all queries. Note that creating a rule that targets the Query
type sets a
maximum maxAge
and swr
for all rules! Since Stellate always uses the
minimum maxAge
and swr
of all the rules that apply to a single query, and
this rule applies to all queries, you can never increase the maxAge
and
swr
of any cached query result to more than 900 (in this example).
import { Config } from 'stellate'
const config: Config = {
config: {
rules: [
{
types: ['Query'],
maxAge: 900,
swr: 900,
description: 'Cache everything',
},
],
},
}
export default config
How to not cache a type?
If you want to make sure a specific type or field is not getting cached at all, add its name to the nonCacheable
config property.
import { Config } from 'stellate'
const config: Config = {
config: {
nonCacheable: [
// Don't cache the `User` type
'User',
// Don't cache the field `comments` on the `Post` type
'Post.comments',
],
},
}
export default config
What happens when multiple rules apply to the same GraphQL query?
Stellate will use the minimum maxAge, and the minimum swr and combine all scopes to cache the query result.
For example, imagine you have these two rules defined:
import { Config } from 'stellate'
const config: Config = {
config: {
rules: [
{
types: ['Post'],
maxAge: 900,
swr: 900,
description: 'Cache Posts publicly',
},
{
types: ['User'],
maxAge: 450,
swr: 1000,
scope: 'AUTHENTICATED',
description: 'Cache User data privately',
},
],
},
}
export default config
And send a query that contains a User and a Post, which means both rules apply:
{
user(id: "...") {
id
posts(first: 10) {
id
}
}
}
Then the query result would be cached with a 450s maxAge
(the minimum of both rules), a 900s swr
(the minimum of both rules) as well as with a scope of AUTHENTICATED (the combination of all the scopes applied via the rules).