a6-recipe-graphql-proxy
>-
Works with
---
name: a6-recipe-graphql-proxy
description: >-
license: Apache-2.0
---
# a6-recipe-graphql-proxy
## Overview
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.
## When to Use
- Routing different GraphQL operations to different backends
- Applying rate limits per operation type (queries vs mutations)
- Restricting which operations specific consumers can execute
- Converting REST endpoints to GraphQL queries (degraphql)
- Adding security layers (auth, rate limiting) to a GraphQL API
## Approach A: Operation-Based Routing
Route GraphQL queries and mutations to different backends using the
`graphql_operation` variable.
### Route queries to read replicas, mutations to primary
```bash
# 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 by operation name
```bash
# 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.
## Approach B: Per-Operation Rate Limiting
Apply different rate limits to queries vs mutations.
```bash
# 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
```
## Approach C: Restrict Operations by Consumer
Use `consumer-restriction` to allow only specific consumers to execute
mutations.
```bash
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
```
## Approach D: REST-to-GraphQL with degraphql
The `degraphql` plugin converts RESTful endpoints into GraphQL queries,
allowing REST clients to consume a GraphQL backend.
### 1. Enable degraphql on a route
```bash
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")`.
### 2. Static query (no variables)
```bash
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
```
## Declarative GraphQL Config
```yaml
# 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
```
```bash
a6 config diff -f apisix-graphql.yaml
a6 config sync -f apisix-graphql.yaml
```
## Gotchas
- **Body size limit** — APISIX parses GraphQL from the request body. Default max
body size is 1 MiB (configurable via `client_max_body_size` in APISIX config).
Large queries may be rejected.
- **Single operation only** — APISIX extracts variables from the **first** operation
in the request. Batched GraphQL queries (multiple operations) are not supported
for routing purposes.
- **No WebSocket subscriptions** — GraphQL subscriptions over WebSocket are not
supported by the built-in GraphQL parsing. You can still proxy WebSocket
connections, but without operation-based routing.
- **POST content types** — GraphQL parsing works with `application/json` (standard)
and `application/graphql` (query in body as text). Other content types are not
parsed.
- **GET requests** — GraphQL variables are read from the `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.
- **degraphql limitations** — the plugin sends a POST with `application/json` to
the upstream, regardless of the original request method. The `variables` field
maps URI path parameters to GraphQL variables by name.
- **Priority for overlapping routes** — when multiple routes match `/graphql` with
different `vars`, use the `priority` field to control matching order. Higher
priority = matched first.
## Verification
```bash
# 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")
```More API Design skills
lark-event
larksuite/cli
Lark/Feishu real-time event listening / subscribing / consuming: stream events as NDJSON via `lark-cli event consume <EventKey>` (covers IM messages/reactions/chat changes, Approval status changes, Task updates, VC meeting started/joined/ended, Minutes generated, Whiteboard updated, etc.). Use for Lark bots, real-time message processing, long-running subscribers, streaming webhook/push handlers. Supports `--max-events` / `--timeout` bounded runs and a stderr ready-marker contract — designed for AI agents running as subprocesses.
lark-contact
larksuite/cli
飞书 / Lark 通讯录:按姓名 / 邮箱解析成 open_id,或按 open_id 反查姓名 / 部门 / 邮箱 / 联系方式 / 个人状态 / 签名,以及按关键词搜索当前用户可见的机器人 / 智能体(agent)。当用户提到一个名字要下一步发消息 / 排日程,或拿到 open_id 想查具体信息时使用。不负责部门树遍历、按部门列员工、组织架构图,这类需求走原生 OpenAPI。
lark-openapi-explorer
larksuite/cli
飞书/Lark 原生 OpenAPI 探索:从官方文档库中挖掘未经 CLI 封装的原生 OpenAPI 接口。当用户的需求无法被现有 lark-* skill 或 lark-cli 已注册命令满足,需要查找并调用原生飞书 OpenAPI 时使用。

