feat(plugin): add graphql-proxy-cache plugin#13435
Open
AlinsRan wants to merge 11 commits into
Open
Conversation
Add a new graphql-proxy-cache plugin that caches GraphQL query responses on disk or in memory, reusing the proxy-cache infrastructure. Key features: - Supports both disk and memory caching strategies - Cache key is bound to conf version, host, route/service ID, and query body - Consumer isolation: each authenticated consumer gets its own cache namespace - Mutation operations bypass the cache automatically - PURGE API to invalidate specific cache entries - Supports GET and POST GraphQL requests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ab5793e to
ed4ac03
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces a new graphql-proxy-cache plugin to cache GraphQL query responses using APISIX’s existing proxy-cache infrastructure, supporting both disk (NGINX proxy_cache) and in-memory (shared dict) strategies, plus a PURGE endpoint exposed via public-api.
Changes:
- Added
apisix/plugins/graphql-proxy-cache.luaimplementing GraphQL request parsing, cache key derivation, mutation bypass, and PURGE API. - Added integration tests in
t/plugin/graphql-proxy-cache/graphql.tand registered the plugin in admin/plugin lists and default plugin configs. - Added English documentation and registered it in the docs sidebar config.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 18 comments.
Show a summary per file
| File | Description |
|---|---|
apisix/plugins/graphql-proxy-cache.lua |
New plugin implementation (GraphQL parsing, cache keying, mutation bypass, PURGE endpoint). |
t/plugin/graphql-proxy-cache/graphql.t |
New test suite covering schema validation, request validation, hit/miss, bypass, and purge. |
t/admin/plugins.t |
Adds the plugin to the expected Admin API plugins list. |
apisix/cli/config.lua |
Registers the plugin in the default plugin set. |
conf/config.yaml.example |
Adds the plugin to the example default plugin list with priority 1009. |
docs/en/latest/plugins/graphql-proxy-cache.md |
New plugin documentation page. |
docs/en/latest/config.json |
Adds the doc page to the docs navigation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- fix typo: cant't -> can't in error message - return specific validation error instead of generic 'no query' - normalize args.query to string when GET params are duplicated - guard header_filter/body_filter against nil upstream_cache_key - move body/cache-key logs from info to debug level - validate strategy param in purge handler - fix test assertions: assert equality not presence for MISS status - fix spelling: zone not exits -> zone not exists in test title
- graph-proxy-cache -> graphql-proxy-cache in error log - file not exits -> file not exists in error log - memory_hanler -> memory_handler variable name
- decode JSON with null_as_nil to handle {"query": null} correctly
- log body_size instead of raw body to avoid PII leakage
- reject empty string route_id/cache_key in purge handler
…nd add tests - Return 400 when URL strategy doesn't match route's configured cache_strategy - Add TEST 20: invalid strategy returns 400 - Add TEST 21: strategy mismatch returns 400 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
nic-6443
previously approved these changes
May 28, 2026
shreemaan-abhishek
previously approved these changes
May 28, 2026
Member
|
Review notes from merge-risk check: [P1]
|
… is enabled and stop logging raw body - Extend ngx_tpl.lua guards so that the upstream_cache_* NGINX variables and proxy_cache* directives are generated when either proxy-cache or graphql-proxy-cache is enabled, preventing undefined-variable failures in deployments that use graphql-proxy-cache without proxy-cache - Replace raw body with body_size in parse-error log lines to avoid leaking request payloads into error.log Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
4f26849
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
membphis
approved these changes
May 28, 2026
shreemaan-abhishek
approved these changes
May 29, 2026
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
This PR adds a new
graphql-proxy-cacheplugin that caches GraphQL query responses on disk or in memory, reusing the existingproxy-cacheinfrastructure.Changes
apisix/plugins/graphql-proxy-cache.lua— plugin implementationt/plugin/graphql-proxy-cache/graphql.t— schema and request validation tests (19 cases)t/plugin/graphql-proxy-cache/disk.t— disk cache hit/miss tests (11 cases)t/plugin/graphql-proxy-cache/memory.t— memory cache hit/miss and consumer isolation tests (15 cases)docs/en/latest/plugins/graphql-proxy-cache.md— English documentationdocs/zh/latest/plugins/graphql-proxy-cache.md— Chinese documentationMotivation
GraphQL APIs have different caching semantics from plain HTTP APIs:
mutationoperations have side effects and must never be cachedThe existing
proxy-cacheplugin works at the HTTP level and cannot distinguish query operations from mutations, nor does it handle GraphQL-over-GET (where the query is in the query string). This plugin fills that gap.How it works
Cache key
conf_version— invalidates the cache automatically when the plugin configuration changeshost+route_id+service_id— ensures two routes with the same query body never share cache entriesidentity— whenconsumer_isolationis enabled (default), this is the consumer name or remote user; otherwise it is an empty stringbody— the normalized GraphQL query bodyMutation bypass
After parsing the GraphQL AST, if any top-level operation is a
mutation, the plugin setsApisix-Cache-Status: BYPASSand skips the cache entirely.Caching infrastructure
The plugin delegates to the same
proxy-cachedisk and memory handlers (apisix.plugins.proxy-cache.disk_handler,apisix.plugins.proxy-cache.memory_handler), so all existing cache zone configuration inconfig.yamlis reused without duplication.Configuration
diskfor NGINXproxy_cache, ormemoryfor a shared dict.config.yaml.memorystrategy. Disk TTL follows upstreamCache-Control/Expires.Set-Cookieheader (memory strategy only).Static configuration
Configure at least one cache zone in
config.yamlbefore enabling this plugin:Usage examples
Cache GraphQL queries on disk (default)
Mutation operations always bypass the cache
Cache in memory with a short TTL
Purge a cached response
Expose the purge endpoint via the
public-apiplugin:Then purge by strategy, route ID, and cache key:
Consumer isolation
With
consumer_isolation: true(default), each consumer gets an isolated cache namespace.Two consumers sending the same query will each get their own
MISSon the first requestand their own
HITon subsequent requests — they never share each other's cached responses:Set
consumer_isolation: falseto let all consumers share the same cache, useful when the upstream response is not user-specific.Checklist