-
Notifications
You must be signed in to change notification settings - Fork 565
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
205 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# @graphql-yoga/plugin-response-cache | ||
|
||
For the documentation check `http://graphql-yoga.com/docs/response-cache` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
--- | ||
id: response-caching | ||
title: Response Caching | ||
sidebar_label: Resposne Caching | ||
--- | ||
|
||
Response caching is a technique for reducing server load by caching GraphQL query operation results. | ||
|
||
## Installation | ||
|
||
<PackageInstall packages={['@graphql-yoga/plugin-response-cache']} /> | ||
|
||
## Quick Start | ||
|
||
```ts | ||
import { createYoga } from 'graphql-yoga' | ||
import { createServer } from 'node:http' | ||
import { useResponseCache } from '@graphql-yoga/plugin-response-cache' | ||
|
||
const yoga = createYoga({ | ||
schema: { | ||
typeDefs: `type Query { slow: String}`, | ||
resolvers: { | ||
Query: { | ||
slow: async () => { | ||
await new Promise((resolve) => setTimeout(resolve, 5000)) | ||
return 'I am slow.' | ||
}, | ||
}, | ||
}, | ||
}, | ||
plugins: [ | ||
useResponseCache({ | ||
// global cache | ||
session: () => null, | ||
}), | ||
], | ||
}) | ||
|
||
const server = createServer(yoga) | ||
server.listen(4000) | ||
``` | ||
|
||
``` | ||
curl -X POST -H 'Content-Type: application/json' http://localhost:4000/graphql \ | ||
-d '{"query":"{__typename}"}' -w '\nTotal time : %{time_total}' | ||
``` | ||
|
||
This will output something like the following: | ||
|
||
``` | ||
{"data":{"slow":"I am slow."}} | ||
Total time:5.026632 | ||
``` | ||
|
||
After executing the same curl statement a second time, the duration is significantly lower. | ||
|
||
``` | ||
{"data":{"slow":"I am slow."}} | ||
Total time:0.007571% | ||
``` | ||
|
||
## Session based caching | ||
|
||
If your GraphQL API returns specific data depending on the viewer's session, you can use the `session` option to cache the response per session. | ||
|
||
```ts | ||
useResponseCache({ | ||
// cache based on the authentication header | ||
session: (request) => request.headers.get('authentication'), | ||
}) | ||
``` | ||
|
||
## TTL | ||
|
||
It is possible to give cached operations a time to live. Either globally, based on [schema coordinates](https://github.com/graphql/graphql-wg/blob/main/rfcs/SchemaCoordinates.md) or object types. | ||
If a query operation result contains multiple objects of the same type, the lowest TTL is picked. | ||
|
||
```ts | ||
useResponseCache({ | ||
session: () => null, | ||
// by default cache all operations for 2 seconds | ||
ttl: 2_000, | ||
ttlPerType: { | ||
// only cache query operations containing User for 500ms | ||
User: 500, | ||
}, | ||
ttlPerSchemaCoordinate: { | ||
// cache operations selecting Query.lazy for 10 seconds | ||
'Query.lazy': 10_000, | ||
}, | ||
}) | ||
``` | ||
|
||
## Invalidatios via Mutation | ||
|
||
When executing a mutation operation the cached query results that contain type entities within the Mutation result will be automatically be invalidated. | ||
|
||
```graphql | ||
mutation { | ||
updateUser(id: 1, newName: "John") { | ||
__typename | ||
id | ||
name | ||
} | ||
} | ||
``` | ||
|
||
```json | ||
{ | ||
"data": { | ||
"updateLaunch": { | ||
"__typename": "User", | ||
"id": "1", | ||
"name": "John" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
All cached query results that contain the type `User` with the id `1` will be invalidated. | ||
|
||
This behavior can be disabled by setting the `invalidateViaMutation` option to `false`. | ||
|
||
```ts | ||
useResponseCache({ | ||
session: (request) => null, | ||
invalidateViaMutation: false, | ||
}) | ||
``` | ||
|
||
## Manual Invalidation | ||
|
||
You can invalidate a type or specific instances of a type using the cache invalidation API. | ||
|
||
In order to use the API, you need to manually instantiate the cache an pass it to the `useResponseCache` plugin. | ||
|
||
```ts | ||
import { | ||
useResponseCache, | ||
createInMemoryCache, | ||
} from '@graphql-yoga/plugin-response-cache' | ||
|
||
const cache = createInMemoryCache() | ||
|
||
useResponseCache({ | ||
session: () => null, | ||
cache, | ||
}) | ||
``` | ||
|
||
Then in your business logic you can call the `invalidate` method on the cache instance. | ||
|
||
Invalidate all GraphQL query results that referance a specific type: | ||
|
||
```ts | ||
cache.invalidate([{ type: 'User' }]) | ||
``` | ||
|
||
Invalidate all GraphQL query results that reference a specific entity of a type: | ||
|
||
```ts | ||
cache.invalidate([{ type: 'User', id: '1' }]) | ||
``` | ||
|
||
Invalidate all GraphQL query results multiple entities in a single call. | ||
|
||
```ts | ||
cache.invalidate([ | ||
{ type: 'Post', id: '1' }, | ||
{ type: 'User', id: '2' }, | ||
]) | ||
``` | ||
|
||
## External Cache | ||
|
||
By default the response cache stores all the cached query results in memory. | ||
|
||
If you want a cache that is shared between multiple server instances you can use the Redis cache implementation. | ||
|
||
<PackageInstall packages={['@envelop/response-cache-redis']} /> | ||
|
||
```ts | ||
import { useResponseCache } from '@graphql-yoga/plugin-response-cache' | ||
import Redis from 'ioredis' | ||
|
||
const redis = new Redis({ | ||
host: 'my-redis-db.example.com', | ||
port: '30652', | ||
password: '1234567890', | ||
}) | ||
|
||
const redis = new Redis('rediss://:1234567890@my-redis-db.example.com:30652') | ||
|
||
const cache = createRedisCache({ redis }) | ||
|
||
useResponseCache({ | ||
session: () => null, | ||
cache, | ||
}) | ||
``` |