graphql-proxy-cache
The graphql-proxy-cache plugin caches GraphQL query responses using disk-based or in-memory caching. It supports GraphQL GET and POST requests.
The plugin generates an MD5 cache key from the plugin configuration version, request host, route ID, service ID, authenticated consumer identity, and complete GraphQL request body. Consumer identity is included by default when APISIX resolves the request to a consumer or remote user.
If a request contains a mutation operation, the plugin will not cache the data. Instead, it adds an Apisix-Cache-Status: BYPASS header to the response to show that the request bypasses the caching mechanism.
Examples
The examples below use the public Countries GraphQL API as an upstream and demonstrate how you can configure graphql-proxy-cache for different scenarios.
Cache Data on Disk
On-disk caching strategy offers the advantages of data persistency when system restarts and having larger storage capacity compared to in-memory cache. It is suitable for applications that prioritize durability and can tolerate slightly larger cache access latency.
The following example demonstrates how you can use graphql-proxy-cache plugin on a route to cache data on disk.
Create a route with the graphql-proxy-cache plugin with the default configuration to cache data on disk:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "graphql-proxy-cache-route",
"uri": "/graphql",
"plugins": {
"graphql-proxy-cache": {}
},
"upstream": {
"type": "roundrobin",
"pass_host": "node",
"scheme": "https",
"nodes": {
"countries.trevorblades.com:443": 1
}
}
}'
services:
- name: graphql-service
routes:
- uris:
- /graphql
name: graphql-proxy-cache-route
plugins:
graphql-proxy-cache: {}
upstream:
type: roundrobin
scheme: https
nodes:
- host: countries.trevorblades.com
port: 443
weight: 1
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: countries-graphql-external-domain
spec:
type: ExternalName
externalName: countries.trevorblades.com
---
apiVersion: apisix.apache.org/v1alpha1
kind: BackendTrafficPolicy
metadata:
namespace: aic
name: countries-graphql-https
spec:
targetRefs:
- name: countries-graphql-external-domain
kind: Service
group: ""
passHost: node
scheme: https
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: graphql-proxy-cache-plugin-config
spec:
plugins:
- name: graphql-proxy-cache
config:
_meta:
disable: false
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: graphql-proxy-cache-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /graphql
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: graphql-proxy-cache-plugin-config
backendRefs:
- name: countries-graphql-external-domain
port: 443
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: countries-graphql-external-domain
spec:
ingressClassName: apisix
scheme: https
passHost: node
externalNodes:
- type: Domain
name: countries.trevorblades.com
port: 443
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: graphql-proxy-cache-route
spec:
ingressClassName: apisix
http:
- name: graphql-proxy-cache-route
match:
paths:
- /graphql
upstreams:
- name: countries-graphql-external-domain
plugins:
- name: graphql-proxy-cache
enable: true
Apply the configuration to your cluster:
kubectl apply -f graphql-proxy-cache-ic.yaml
Send a request with a GraphQL query to verify:
curl -i "http://127.0.0.1:9080/graphql" -X POST \
-H "Content-Type: application/json" \
-d '{"query": "query { country(code: \"US\") { name capital } }"}'
You should see an HTTP/1.1 200 OK response with the following headers, showing the plugin is successfully enabled:
APISIX-Cache-Key: 5908e74856ea02835af198678b879a71
Apisix-Cache-Status: MISS
As there is no cache available before the first response, Apisix-Cache-Status: MISS is shown. The exact cache key depends on your configuration.
Send the same request again within the cache TTL window. You should see an HTTP/1.1 200 OK response with the following headers, showing the cache is hit:
APISIX-Cache-Key: 5908e74856ea02835af198678b879a71
Apisix-Cache-Status: HIT
Wait for the cache to expire after the TTL and send the same request again. You should see an HTTP/1.1 200 OK response with the following headers, showing the cache has expired:
APISIX-Cache-Key: 5908e74856ea02835af198678b879a71
Apisix-Cache-Status: EXPIRED
Cache Data in Memory
In-memory caching strategy offers the advantage of low-latency access to the cached data, as retrieving data from RAM is faster than retrieving data from disk storage. It also works well for storing temporary data that does not need to be persisted long-term, allowing for efficient caching of frequently changing data.
The following example demonstrates how you can use graphql-proxy-cache plugin on a route to cache data in memory.
Create a route with graphql-proxy-cache enabled and configure it to use memory-based caching:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "graphql-proxy-cache-route",
"uri": "/graphql",
"plugins": {
"graphql-proxy-cache": {
"cache_strategy": "memory",
"cache_zone": "memory_cache",
"cache_ttl": 10
}
},
"upstream": {
"type": "roundrobin",
"pass_host": "node",
"scheme": "https",
"nodes": {
"countries.trevorblades.com:443": 1
}
}
}'
services:
- name: graphql-service
routes:
- uris:
- /graphql
name: graphql-proxy-cache-route
plugins:
graphql-proxy-cache:
cache_strategy: memory
cache_zone: memory_cache
cache_ttl: 10
upstream:
type: roundrobin
scheme: https
nodes:
- host: countries.trevorblades.com
port: 443
weight: 1
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: countries-graphql-external-domain
spec:
type: ExternalName
externalName: countries.trevorblades.com
---
apiVersion: apisix.apache.org/v1alpha1
kind: BackendTrafficPolicy
metadata:
namespace: aic
name: countries-graphql-https
spec:
targetRefs:
- name: countries-graphql-external-domain
kind: Service
group: ""
passHost: node
scheme: https
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: graphql-proxy-cache-plugin-config
spec:
plugins:
- name: graphql-proxy-cache
config:
cache_strategy: memory
cache_zone: memory_cache
cache_ttl: 10
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: graphql-proxy-cache-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /graphql
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: graphql-proxy-cache-plugin-config
backendRefs:
- name: countries-graphql-external-domain
port: 443
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: countries-graphql-external-domain
spec:
ingressClassName: apisix
scheme: https
passHost: node
externalNodes:
- type: Domain
name: countries.trevorblades.com
port: 443
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: graphql-proxy-cache-route
spec:
ingressClassName: apisix
http:
- name: graphql-proxy-cache-route
match:
paths:
- /graphql
upstreams:
- name: countries-graphql-external-domain
plugins:
- name: graphql-proxy-cache
enable: true
config:
cache_strategy: memory
cache_zone: memory_cache
cache_ttl: 10
Apply the configuration to your cluster:
kubectl apply -f graphql-proxy-cache-ic.yaml
❶ cache_strategy: set to memory for in-memory setting.
❷ cache_zone: set to the name of an in-memory cache zone.
❸ cache_ttl: set the time to live for the in-memory cache.
Send a request with a GraphQL query to verify:
curl "http://127.0.0.1:9080/graphql" -i -X POST \
-H "Content-Type: application/json" \
-d '{"query": "query { country(code: \"US\") { name capital } }"}'
You should see an HTTP/1.1 200 OK response with the following headers, showing the plugin is successfully enabled:
APISIX-Cache-Key: a661316c4b1b70ae2db5347743dec6b6
Apisix-Cache-Status: MISS
As there is no cache available before the first response, Apisix-Cache-Status: MISS is shown. The exact cache key depends on your configuration.
Send the same request again within the cache TTL window. You should see an HTTP/1.1 200 OK response with the following headers, showing the cache is hit:
APISIX-Cache-Key: a661316c4b1b70ae2db5347743dec6b6
Apisix-Cache-Status: HIT
Remove Cache Manually
While most of the time it is not necessary, there may be situations where you would want to manually remove cached data.
The following example demonstrates how you can use the public-api plugin to expose the /apisix/plugin/graphql-proxy-cache/{cache_strategy}/{route_id}/{key} endpoint created by the graphql-proxy-cache plugin. The example also enables key-auth so that only an authenticated operator can purge cached responses.
Create a consumer with a key-auth credential and a route that matches the URI /apisix/plugin/graphql-proxy-cache/*:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "cache-operator"
}'
curl "http://127.0.0.1:9180/apisix/admin/consumers/cache-operator/credentials" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "cache-operator-key-auth",
"plugins": {
"key-auth": {
"key": "purge-key"
}
}
}'
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "graphql-cache-purge",
"uri": "/apisix/plugin/graphql-proxy-cache/*",
"plugins": {
"key-auth": {},
"public-api": {}
}
}'
consumers:
- username: cache-operator
credentials:
- name: cache-operator-key-auth
type: key-auth
config:
key: purge-key
services:
- name: graphql-cache-purge-service
routes:
- name: graphql-cache-purge-route
uris:
- /apisix/plugin/graphql-proxy-cache/*
plugins:
key-auth: {}
public-api: {}
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: apisix.apache.org/v1alpha1
kind: Consumer
metadata:
namespace: aic
name: cache-operator
spec:
gatewayRef:
name: apisix
credentials:
- type: key-auth
name: cache-operator-key-auth
config:
key: purge-key
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: graphql-cache-purge-plugin-config
spec:
plugins:
- name: key-auth
config:
_meta:
disable: false
- name: public-api
config:
_meta:
disable: false
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: graphql-cache-purge-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /apisix/plugin/graphql-proxy-cache/
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: graphql-cache-purge-plugin-config
apiVersion: apisix.apache.org/v2
kind: ApisixConsumer
metadata:
namespace: aic
name: cache-operator
spec:
ingressClassName: apisix
authParameter:
keyAuth:
value:
key: purge-key
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: graphql-cache-purge-route
spec:
ingressClassName: apisix
http:
- name: graphql-cache-purge-route
match:
paths:
- /apisix/plugin/graphql-proxy-cache/*
plugins:
- name: key-auth
enable: true
- name: public-api
enable: true
Apply the configuration to your cluster:
kubectl apply -f graphql-proxy-cache-ic.yaml
Send the disk-cache request and save the generated APISIX-Cache-Key response header:
CACHE_KEY=$(curl -sS -D - -o /dev/null "http://127.0.0.1:9080/graphql" -X POST \
-H "Content-Type: application/json" \
-d '{"query": "query { country(code: \"US\") { name capital } }"}' | \
awk 'tolower($1) == "apisix-cache-key:" {gsub("\\r", "", $2); print $2}')
Send a PURGE request using that value and the ID of the route containing graphql-proxy-cache:
curl -i "http://127.0.0.1:9080/apisix/plugin/graphql-proxy-cache/disk/graphql-proxy-cache-route/${CACHE_KEY}" -X PURGE \
-H "apikey: purge-key"
The Admin API and ADC examples use graphql-proxy-cache-route as the route ID. For an Ingress Controller deployment, replace it with the generated APISIX route ID.
An HTTP/1.1 200 OK response verifies that the cache corresponding to the key is successfully removed.
If you send the same PURGE request again, you should see an HTTP/1.1 404 Not Found response, showing there is no cache on disk with this cache key after the cache removal.
Responses with a Vary header can produce multiple cache variants. A successful PURGE response confirms removal of the targeted cache entry, but does not guarantee that every variant was removed.