# Caching
If some fields in your schema are expensive or slow to compute, it can be beneficial to cache their result. Use the @cache directive to instruct Lighthouse to cache the result of a resolver.
The cache is created on the first request and is cached forever by default. Use this for values that seldom change and take long to fetch/compute.
type Query {
highestKnownPrimeNumber: Int! @cache
}
Set an expiration time to invalidate the cache after the given number of seconds.
type Query {
temperature: Int! @cache(maxAge: 300)
}
You can limit the cache to the authenticated user making the request by marking it as private. This makes sense for data that is specific to a certain user.
type Query {
todos: [ToDo!]! @cache(private: true)
}
# Clear cache
To enable this feature, you need to use a cache store that supports cache tags (opens new window)
and enable cache tags in config/lighthouse.php
:
/*
|--------------------------------------------------------------------------
| Cache Directive Tags
|--------------------------------------------------------------------------
|
| Should the `@cache` directive use a tagged cache?
|
*/
'cache_directive_tags' => true,
Now, you can place the @clearCache directive on mutation fields. When they are queried, they will invalidate all cache entries associated with a calculated tag. Depending on the effect of the mutation, you can clear different tags.
Update the cache associated with a given type without a specific ID:
This does not invalidate cache entries related to the type and ID.
type Mutation {
updateSiteStatistics(input: SiteInput!): Site @clearCache(type: "Site")
}
If your mutation affects only a certain ID, specify the source for that ID:
type Mutation {
updatePost(input: UpdatePostInput!): Post!
@clearCache(type: "Post", idSource: { argument: "input.id" })
}
If your mutation affects multiple entities, you can use the result as the source of IDs:
type Mutation {
updatePosts(search: String!, newValue: Int!): [Post!]!
@clearCache(type: "Post", idSource: { field: "*.id" })
}
If your mutation only affects a single field, you can clear tags that are specific for that:
type Mutation {
updatePost(id: ID!, title: String!): Post!
@clearCache(type: "Post", idSource: { argument: "id" }, field: "title")
}
If your mutation affects multiple levels of cache, you can apply this directive repeatedly.
# Cache key
When generating a cached result for a resolver, Lighthouse produces a unique key for each type.
By default, Lighthouse will look for a field of type ID
on the parent to generate the key
for a field with @cache.
This directive allows to use a different field (i.e., an external API id):
type GithubProfile {
username: String @cacheKey
repos: [Repository] @cache
}
# Implementing your own cache key generator
In one of your application service providers, bind the Nuwave\Lighthouse\Cache\CacheKeyAndTags.php
(opens new window)
interface to your cache key generator class:
$this->app->bind(CacheKeyAndTags::class, YourOwnCacheKeyGenerator::class);
You can extend Nuwave\Lighthouse\Cache\CacheKeyAndTagsGenerator.php
(opens new window)
to override certain methods, or implement the interface from scratch.
# HTTP Cache-Control header
Experimental: not enabled by default, not guaranteed to be stable.
Register the service provider Nuwave\Lighthouse\CacheControl\CacheControlServiceProvider
,
see registering providers in Laravel (opens new window).
You can change the Cache-Control
header (opens new window) of your response
regardless of @cache
by adding the @cacheControl directive to a field.
The directive can be defined on the field-level or type-level.
Note that field-level settings override type-level settings.
The final header settings are calculated based on these rules:
max-age
equals the lowestmaxAge
among all fields. If that value is 0,no-cache
is used instead- visibility is
public
unless the scope of a queried field isPRIVATE
The following defaults apply:
- non-scalar fields
maxAge
is 0 - root fields
maxAge
is 0 andscope
isPRIVATE
- the directive default is prior to the field default
For more details check Apollo (opens new window).
Given the following example schema:
type User {
tasks: [Task!]! @hasMany @cacheControl(maxAge: 50, scope: PUBLIC)
}
type Company @cacheControl(maxAge: 40, scope: PUBLIC) {
users: [User!]! @hasMany @cacheControl(maxAge: 25, scope: PUBLIC)
}
type Task {
id: ID @cacheControl(maxAge: 10, scope: PUBLIC)
name: String @cacheControl(maxAge: 0, inheritMaxAge: true)
description: String @cacheControl
}
type Query {
me: User! @auth @cacheControl(maxAge: 5, scope: PRIVATE)
companies: [Company!]!
publicCompanies: [Company!]! @cacheControl(maxAge: 15)
}
The Cache-Control headers for some queries will be:
# Cache-Control header: max-age: 5, PRIVATE
{
# 5, PRIVATE
me {
# 50, PUBLIC
tasks {
# 10, PUBLIC
id
}
}
}
# Cache-Control header: no-cache, PRIVATE
{
# 5, PRIVATE
me {
# 50, PUBLIC
tasks {
# 0, PUBLIC
description
}
}
}
# Cache-Control header: no-cache, private
{
# 40, PUBLIC
companies {
# 25, PUBLIC
users {
# 50, PUBLIC
tasks {
# 10, PUBLIC
id
}
}
}
}
# Cache-Control header: maxAge: 10, public
{
# 15, PUBLIC
publicCompanies {
# 25, PUBLIC
users {
# 50, PUBLIC
tasks {
# 10, PUBLIC
id
}
}
}
}
# Cache-Control header: maxAge: 15, public
{
# 15, PUBLIC
publicCompanies {
# 25, PUBLIC
users {
# 50, PUBLIC
tasks {
# 50, PUBLIC
name
}
}
}
}