Edge Caching
Edge caching is a performance optimization strategy implemented in your GraphQL API to cache responses in proximity to the end-users. This technique significantly cuts down overall latency, augmenting the speed of data delivery.
Caching is configured using the @cache
directive.
Caching can be configured globally, by type, or field using the configurations below. This gives you an advanced tool for customizing caching behavior, enabling you to fine-tune the settings to optimally meet your project needs.
Global configuration applies caching to all types and fields that are returned by GraphQL queries. This is extremely powerful when used with API connectors.
import { config } from '@grafbase/sdk'
export default config({
cache: {
rules: [
{
types: ['Post', 'User']
}
]
}
})
In the example below we will cache the Post
model and any of its fields for 60 seconds.
import { config, g } from '@grafbase/sdk'
g.model('Post', {
title: g.string(),
slug: g.string(),
content: g.string()
}).cache({
maxAge: 60,
staleWhileRevalidate: 60
})
In the example below we will cache the Post
model and any of its fields for 60 seconds, but only for 30 if the response contains the field content
.
import { config, g } from '@grafbase/sdk'
g.model('Post', {
title: g.string(),
slug: g.string(),
content: g.string().cache({ maxAge: 30, staleWhileRevalidate: 60 })
}).cache({
maxAge: 60,
staleWhileRevalidate: 60
})
The configuration for caching rules are outlined below:
The types
field is required for caching responses and allows targeting specific types.
It supports three formats: a single String
for a single type, an array of String
for multiple types, or a structured array of rules for targeting fields and types.
import { config } from '@grafbase/sdk'
export default config({
cache: {
rules: [
{
types: ['Post']
}
]
}
})
Use the maxAge
value to specify how long (in seconds) the cached entry is considered to be fresh.
import { config } from '@grafbase/sdk'
export default config({
cache: {
rules: [
{
maxAge: 60
}
]
}
})
The lowest value for maxAge
always wins for nested cache rules.
Use the staleWhileRevalidate
value to specify how long (in seconds) the cached entry is considered to be stale.
import { config } from '@grafbase/sdk'
export default config({
cache: {
rules: [
{
maxAge: 30,
staleWhileRevalidate: 60
}
]
}
})
The mutationInvalidation
strategy provides a robust method for purging cached entries associated with specific tags. When implemented, the Edge Gateway scrutinizes GraphQL query responses to extract necessary data, consequently adorning the cache entries with appropriate tags.
To configure mutationInvalidation
, simply add the optional mutationInvalidation
attribute and value. The mutation response will then trigger a purge for any cache entry referencing the mutated type.
In the example below, Grafbase will cache responses for postCollection
and post
for 120 + 10
seconds. When a Post
is mutated, the corresponding tag
(id
of the product) will get invalidated.
import { config, g } from '@grafbase/sdk'
export default config({
schema: g,
cache: {
rules: [
{
maxAge: 120,
staleWhileRevalidate: 60,
types: ['Post'],
mutationInvalidation: 'entity'
},
{
maxAge: 10,
staleWhileRevalidate: 10,
types: [{ name: 'Query', fields: ['postCollection', 'post'] }]
}
]
}
})
The new mutationInvalidation
property accepts multiple options, and works when configuring cachine globally or per type/database model:
entity
— Triggered by mutations for the specified type, this will invalidate any cache entry containing the selected field. By default, it utilizes theid
.list
— Triggered by mutations for the specified type, this will invalidate any cache entry containing lists of the corresponding type.type
— Triggered by mutations for the specified type, this will invalidate any cache entry containing the type.
By default the cache uses the id
to tag entities. If you need to specify a field that is something else, you can do that by passing an object to the mutationInvalidation
field:
import { config, g } from '@grafbase/sdk'
export default config({
schema: g,
cache: {
rules: [
{ maxAge: 10, types: "Product", mutationInvalidation: {
field: '_id'
} },
{
maxAge: 10
types: [{ name: "Query", fields: ["products", "product"] }]
}
]
}
})
The lowest maxAge
value wins.
Cache entries transition through various states during their lifespan. The cache key is computed using the GraphQL query, variables, request authentication, and request domain.
The total time of a cache entry (TTL) is: maxAge + staleWhileRevalidate
. After this TTL elapses, entries are evicted and deleted from cache.
The list below outlines the expected states visible in x-grafbase-cache
response header:
x-grafbase-cache: MISS
No cache key found. Forwarding request to upstream for response retrieval and asynchronous cache put to minimize latency.
x-grafbase-cache: HIT
The cache contains a key that matches the incoming request. Clients will receive the response from the cache.
x-grafbase-cache: UPDATING
A matching key was found in the cache for the incoming request, but it has exceeded the maxAge
limit. Clients will receive the cached response while we asynchronously refresh the entry.
x-grafbase-cache: STALE
The cache contains a key that matches the incoming request, but it has exceeded the maxAge
limit and previous attempts to refresh it have failed.
You can also cache introspection queries by using the the following global configuration:
import { config } from '@grafbase/sdk'
export default config({
cache: {
rules: [
{
maxAge: 60
staleWhileRevalidate: 60
types: [{ name: "Query", fields: ["__schema"] }]
}
]
}
})
More granular control over the purging of the cache can be done via the Admin API.
The Admin API URL is available at: https://[project-slug].grafbase.app/admin
.
You'll need to send the header x-api-key
with an API key for your project.
This API is typically useful if you mutate data not using the GraphQL API.
If you use use the GraphQL API to mutate data, you should consider using the automatic
mutationInvalidation
option instead.
You can use the Admin API to purge a type
, list
, and entity
. The format of each input type
consists of:
- PurgeTypeInput — Purges all cached values that have the provided
Type
- PurgeListInput — Purges all cached values that have lists of the provided
Type
- PurgeEntityInput — Purges all cached values that have the provided
Type
and the given fields
mutation {
cachePurgeTypes(input: { type: { type: "MyTypename" } }) {
tags
}
}
You can purge the entire cache for your project using the cachePurgeAll
mutation:
mutation {
cachePurgeAll {
hostname
}
}