skills/a6-recipe-graphql-proxy/SKILL.md
Recipe skill for implementing GraphQL proxying patterns using the a6 CLI. Covers operation-based routing with built-in GraphQL variables, per-operation rate limiting, REST-to-GraphQL conversion with the degraphql plugin, and security patterns for GraphQL APIs.
npx skillsauth add moonming/a6 a6-recipe-graphql-proxyInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
APISIX provides built-in GraphQL support through three variables that let you route and apply policies based on GraphQL query content — without parsing GraphQL yourself:
| Variable | Description | Example Value |
|----------|-------------|---------------|
| graphql_name | Operation name from the query | "getUser" |
| graphql_operation | Operation type | "query", "mutation" |
| graphql_root_fields | Top-level fields requested | ["user", "orders"] |
These variables are extracted automatically from POST requests with
Content-Type: application/json or application/graphql, and from GET
requests with a query parameter.
Route GraphQL queries and mutations to different backends using the
graphql_operation variable.
# Queries → read replica
a6 route create -f - <<'EOF'
{
"id": "graphql-queries",
"uri": "/graphql",
"vars": [["graphql_operation", "==", "query"]],
"upstream": {
"type": "roundrobin",
"nodes": { "graphql-read-replica:4000": 1 }
}
}
EOF
# Mutations → primary database
a6 route create -f - <<'EOF'
{
"id": "graphql-mutations",
"uri": "/graphql",
"vars": [["graphql_operation", "==", "mutation"]],
"upstream": {
"type": "roundrobin",
"nodes": { "graphql-primary:4000": 1 }
}
}
EOF
# Route the expensive "analytics" query to a dedicated backend
a6 route create -f - <<'EOF'
{
"id": "graphql-analytics",
"uri": "/graphql",
"vars": [["graphql_name", "==", "getAnalytics"]],
"priority": 10,
"upstream": {
"type": "roundrobin",
"nodes": { "analytics-backend:4000": 1 }
}
}
EOF
The priority field ensures this route is matched before a generic /graphql route.
Apply different rate limits to queries vs mutations.
# Queries: 1000 req/min
a6 route create -f - <<'EOF'
{
"id": "graphql-query-limited",
"uri": "/graphql",
"vars": [["graphql_operation", "==", "query"]],
"plugins": {
"key-auth": {},
"limit-count": {
"count": 1000,
"time_window": 60,
"key_type": "var",
"key": "consumer_name",
"rejected_code": 429
}
},
"upstream": {
"type": "roundrobin",
"nodes": { "graphql-backend:4000": 1 }
}
}
EOF
# Mutations: 100 req/min (more restrictive)
a6 route create -f - <<'EOF'
{
"id": "graphql-mutation-limited",
"uri": "/graphql",
"vars": [["graphql_operation", "==", "mutation"]],
"plugins": {
"key-auth": {},
"limit-count": {
"count": 100,
"time_window": 60,
"key_type": "var",
"key": "consumer_name",
"rejected_code": 429,
"rejected_msg": "Mutation rate limit exceeded"
}
},
"upstream": {
"type": "roundrobin",
"nodes": { "graphql-backend:4000": 1 }
}
}
EOF
Use consumer-restriction to allow only specific consumers to execute
mutations.
a6 route create -f - <<'EOF'
{
"id": "graphql-mutations-restricted",
"uri": "/graphql",
"vars": [["graphql_operation", "==", "mutation"]],
"plugins": {
"key-auth": {},
"consumer-restriction": {
"whitelist": ["admin-user", "service-account"],
"rejected_code": 403,
"rejected_msg": "Mutations not allowed for your account"
}
},
"upstream": {
"type": "roundrobin",
"nodes": { "graphql-backend:4000": 1 }
}
}
EOF
The degraphql plugin converts RESTful endpoints into GraphQL queries,
allowing REST clients to consume a GraphQL backend.
a6 route create -f - <<'EOF'
{
"id": "rest-to-graphql-users",
"uri": "/users/:id",
"methods": ["GET"],
"plugins": {
"degraphql": {
"query": "query getUser($id: ID!) { user(id: $id) { id name email } }",
"variables": ["id"]
}
},
"upstream": {
"type": "roundrobin",
"nodes": { "graphql-backend:4000": 1 }
}
}
EOF
REST clients call GET /users/123 and receive the GraphQL response
for user(id: "123").
a6 route create -f - <<'EOF'
{
"id": "rest-to-graphql-stats",
"uri": "/stats",
"methods": ["GET"],
"plugins": {
"degraphql": {
"query": "{ systemStats { cpu memory uptime } }"
}
},
"upstream": {
"type": "roundrobin",
"nodes": { "graphql-backend:4000": 1 }
}
}
EOF
# apisix-graphql.yaml
routes:
- id: graphql-queries
uri: "/graphql"
vars: [["graphql_operation", "==", "query"]]
plugins:
key-auth: {}
limit-count:
count: 1000
time_window: 60
key_type: var
key: consumer_name
upstream:
type: roundrobin
nodes:
"graphql-read-replica:4000": 1
- id: graphql-mutations
uri: "/graphql"
vars: [["graphql_operation", "==", "mutation"]]
plugins:
key-auth: {}
limit-count:
count: 100
time_window: 60
key_type: var
key: consumer_name
consumer-restriction:
whitelist: ["admin-user", "service-account"]
upstream:
type: roundrobin
nodes:
"graphql-primary:4000": 1
a6 config diff -f apisix-graphql.yaml
a6 config sync -f apisix-graphql.yaml
client_max_body_size in APISIX config).
Large queries may be rejected.application/json (standard)
and application/graphql (query in body as text). Other content types are not
parsed.query URL parameter
(URL-encoded GraphQL query string).vars matching — the vars field on a route accepts an array of conditions.
Each condition is ["variable", "operator", "value"]. Multiple conditions are
AND-ed together.application/json to
the upstream, regardless of the original request method. The variables field
maps URI path parameters to GraphQL variables by name./graphql with
different vars, use the priority field to control matching order. Higher
priority = matched first.# Test query routing
curl -X POST http://localhost:9080/graphql \
-H "Content-Type: application/json" \
-H "apikey: my-key" \
-d '{"query": "query getUser { user(id: 1) { name } }"}'
# Test mutation routing
curl -X POST http://localhost:9080/graphql \
-H "Content-Type: application/json" \
-H "apikey: my-key" \
-d '{"query": "mutation createUser { createUser(name: \"test\") { id } }"}'
# Test rate limiting (should 429 after exceeding limit)
for i in $(seq 1 1001); do
curl -s -o /dev/null -w "%{http_code}\n" \
-X POST http://localhost:9080/graphql \
-H "Content-Type: application/json" \
-H "apikey: my-key" \
-d '{"query": "{ users { id } }"}'
done
# Test REST-to-GraphQL
curl http://localhost:9080/users/123
# Returns GraphQL response for user(id: "123")
tools
Core skill for working with the a6 CLI — the Apache APISIX command-line tool. Provides project conventions, command patterns, architecture overview, and development workflow. Load this skill when working on a6 source code, adding new commands, writing tests, or modifying any a6 component.
tools
Recipe skill for implementing multi-tenant API gateway patterns using the a6 CLI. Covers tenant isolation via Consumer Groups, host/path/header-based routing, per-tenant rate limiting, context forwarding with proxy-rewrite, and declarative config sync workflows for multi-tenant management.
tools
Recipe skill for configuring mutual TLS (mTLS) using the a6 CLI. Covers SSL certificate management, upstream mTLS to backend services, client certificate verification, and end-to-end mTLS setup from client through APISIX to upstream.
tools
Recipe skill for configuring upstream health checks using the a6 CLI. Covers active health checks (HTTP probing), passive health checks (response analysis), combining both, configuring healthy/unhealthy thresholds, and monitoring upstream node status.