Apollo Router Config Generator
Apollo Router is a high-performance graph router written in Rust for running Apollo Federation 2 supergraphs. It sits in front of your subgraphs and handles query planning, execution, and response composition.
This skill generates version-correct configuration. Router v1 and v2 have incompatible config schemas in several critical sections (CORS, JWT auth, connectors). Always determine the target version before generating any config.
Step 1: Version Selection
Ask the user before generating any config:
Which Apollo Router version are you targeting?
[1] Router v2.x (recommended — current LTS, required for Connectors)
[2] Router v1.x (legacy — end-of-support announced, security patches only)
[3] Not sure — help me decide
If the user picks [3], display:
Quick guide:
• Pick v2 if: you're starting fresh, using Apollo Connectors for REST APIs,
or want backpressure-based overload protection.
• Pick v1 if: you have an existing deployment and haven't migrated yet.
Note: Apollo ended active support for v1.x. The v2.10 LTS (Dec 2025)
is the current baseline. Migration is strongly recommended.
Tip: If you have an existing router.yaml, you can auto-migrate it:
router config upgrade router.yaml
Store the selection as ROUTER_VERSION=v1|v2 to gate all subsequent template generation.
Step 2: Environment Selection
Ask: Production or Development?
- Production: security-hardened defaults (introspection off, sandbox off, homepage off, subgraph errors hidden, auth required, health check on)
- Development: open defaults (introspection on, sandbox on, errors exposed, text logging)
Load the appropriate base template from:
templates/{version}/production.yaml
templates/{version}/development.yaml
Step 3: Feature Selection
Ask which features to include:
Step 4: Gather Parameters
For each selected feature, collect required values.
- Use section templates from
templates/{version}/sections/ for auth, cors, headers, limits, telemetry, and traffic-shaping.
- For Connectors in v2, use
templates/v2/sections/connectors.yaml as the source.
- For APQ and subscriptions, copy the snippet from the selected base template (
templates/{version}/production.yaml or templates/{version}/development.yaml) or from references.
- Only offer Connectors when
ROUTER_VERSION=v2.
CORS
- List of allowed origins (never use
"*" for production)
JWT Authentication
- JWKS URL
- Issuer(s) — note: v1 uses singular
issuer, v2 uses plural issuers array
Declarative Authorization (field-level)
Field- and type-level access control enforced in the router, via the @authenticated, @requiresScopes, and @policy directives applied in subgraph schemas. This is the layer that the global authorization.require_authentication gate cannot express. It is a GraphOS feature (Enterprise; Developer/Standard plans require Router v2.6.0+) and requires a router connected to GraphOS. Directives are enabled by default — config only turns them off.
Confirm prerequisites before recommending these:
- Router connected to GraphOS (Router v1.29.1+; Developer/Standard plans need v2.6.0+).
- A claims source. Directives evaluate the claims at the
apollo::authentication::jwt_claims context key. Populate it via JWT authentication (configure that feature too) or a coprocessor that injects claims.
@policy additionally requires a Supergraph plugin (Rhai script or coprocessor) to evaluate each policy — the router extracts required policies into apollo::authorization::required_policies but does not decide them itself.
Ask:
- Which fields/types need protection, and at what level? (
@authenticated = any valid identity; @requiresScopes = specific scopes; @policy = custom logic.)
- Where do scopes/claims come from? (JWT claims vs. coprocessor-injected.)
The directives live in the subgraph schemas, not in router.yaml. The router config only enables/disables the feature and (for @policy) wires the evaluating plugin. See references/configuration.md → Authorization.
Persisted Query Safelisting (GraphOS PQL)
Not the same as APQ. APQ (apq) is a runtime bandwidth optimization that caches any operation a client sends — it provides no security. Safelisting uses a GraphOS-managed Persisted Query List (PQL) that clients register at build time; the router then rejects operations not on the list. This is the "persisted query safelisting" security control. It is a GraphOS feature requiring a router connected to GraphOS (APOLLO_KEY + APOLLO_GRAPH_REF).
Pick a security level (increasing restrictiveness):
| Level |
Config |
Behavior |
| Audit (recommended first) |
persisted_queries.log_unknown: true |
Logs unregistered operations; rejects nothing. Use to confirm all clients are registered before enforcing. |
| Safelist |
safelist.enabled: true |
Rejects operations not in the PQL. IDs and full strings both accepted if registered. |
| Safelist, IDs only |
safelist.enabled: true + require_id: true |
Rejects unregistered operations and any freeform operation string, even if the string is registered. |
Then gather:
- Is the router GraphOS-connected? Safelisting needs the PQL fetched from GraphOS (or
local_manifests for offline licenses).
- Have clients published their operations to the PQL (via
rover persisted-queries publish in their CI/CD)? If not, start in audit mode.
- When enabling
safelist, APQ must be disabled (apq.enabled: false) — they are mutually exclusive.
Config key history: GA persisted_queries since v1.32.0 (was preview_persisted_queries in v1.25.0–v1.32.0); GA in all v2. See references/configuration.md → Persisted Query Safelisting.
Connectors (v2 only)
- Subgraph name and source name (used as
connectors.sources.<subgraph>.<source>)
- Optional
$config values for connector runtime configuration
- If migrating old v2 preview config, rename
preview_connectors to connectors
Operation Limits
Present the tuning guidance:
Operation depth limit controls how deeply nested a query can be.
Router default: 100 (permissive — allows very deep queries)
Recommended starting point: 50
Lower values (15–25) are more secure but will reject legitimate queries
in schemas with deep entity relationships or nested fragments.
Higher values (75–100) are safer for compatibility but offer less
protection against depth-based abuse.
Tip: Run your router in warn_only mode first to see what depths your
real traffic actually uses, then tighten:
limits:
warn_only: true
What max_depth would you like? [default: 50]
The same principle applies to max_height, max_aliases, and max_root_fields.
Telemetry
- OTEL collector endpoint (default:
http://otel-collector:4317)
- Prometheus listen port (default:
9090)
- Trace sampling rate (default:
0.1 = 10%)
Traffic Shaping
- Client-facing rate limit capacity (default: 1000 req/s)
- Router timeout (default: 60s)
- Subgraph timeout (default: 30s)
Response Caching (v2 only, v2.6.0+)
Security: data leakage risk. Before generating any response cache config, you MUST ask the user which types and fields return user-specific data. Cached data defaults to shared — subgraph responses without Cache-Control: private are visible to all users. User-specific subgraphs must return Cache-Control: private and have private_id configured on the router.
- Ask: Which subgraphs serve user-specific data? (e.g., accounts, profiles, carts)
- Ask: How do you identify users? (JWT
sub claim, session token, API key)
- Redis URL (default:
redis://localhost:6379)
- Default TTL (default:
5m)
- Enable active invalidation? If yes: invalidation listen address and shared key
- Use section template:
templates/v2/sections/response-caching.yaml
- For security requirements, schema directives, and advanced config:
references/response-caching.md (start with the Security section)
Step 5: Generate Config
- Load the correct version template from
templates/{version}/
- Assemble section templates for supported sectioned features, then merge base-template snippets for APQ/subscriptions as needed
- Inject user-provided parameters
- Add a comment block at the top stating the target version
Step 6: Validate
Run the post-generation checklist:
Required Validation Gate (always run)
After generating or editing any router.yaml, you MUST:
- Run
validation/checklist.md and report pass/fail for each checklist item.
- Run
router config validate <path-to-router.yaml> if Router CLI is available.
- If Router CLI is unavailable, state that explicitly and still complete the checklist.
- Do not present the configuration as final until validation is completed.
Configuration as Code (git + CI/CD)
router.yaml is the router's contract with every request — treat it like application code, not an ops afterthought. Whenever you generate or edit config, steer the user toward this workflow:
- Commit
router.yaml to version control. It should live in git alongside the service, with changes reviewed via pull request. This gives you history, blame, and rollback for the most safety-critical file in the API layer.
- Never commit secrets. Keep
APOLLO_KEY, JWKS URLs, Redis URLs, and invalidation keys out of the file — reference them with ${env.*} expansion and inject at deploy time. The committed file should be safe to read by anyone with repo access.
- Validate in CI. Run
router config validate router.yaml on every PR so a malformed or version-mismatched config fails the build before it ships. Pin the Router version used in CI to the version you deploy.
- Pair config changes with schema checks. Schema changes flow through
rover subgraph check / rover subgraph publish (the rover skill); config changes flow through this validate-in-CI gate. Both gate the same deploy.
- Promote the same file across environments. Differences between dev and prod should be expressed through env vars, not divergent committed files, so what you reviewed is what runs.
A minimal CI step (provide actual commands only if asked):
# Validate router config on every pull request
- run: router config validate router.yaml
Step 7: Conditional Next Steps Handoff
After answering any Apollo Router request (config generation, edits, validation, or general Router guidance), decide whether the user already has runnable prerequisites:
- GraphOS-managed path:
APOLLO_KEY + APOLLO_GRAPH_REF, or
- Local path: a composed
supergraph.graphql plus reachable subgraphs
If prerequisites are already present, do not add extra handoff text.
If prerequisites are missing or unknown, end with a concise Next steps handoff (1-3 lines max) that is skill-first and command-free:
- Suggest the
rover skill to compose or fetch the supergraph schema.
- Suggest continuing with
apollo-router once the supergraph is ready to validate and run with the generated config.
- If subgraphs are missing, suggest
apollo-server, graphql-schema, and graphql-operations skills to scaffold and test.
Do not include raw shell commands in this handoff unless the user explicitly asks for commands.
Quick Start (skill-first)
- Use this
apollo-router skill to generate or refine router.yaml for your environment.
- Choose a runtime path:
- GraphOS-managed path: provide
APOLLO_KEY and APOLLO_GRAPH_REF (no local supergraph composition required).
- Local supergraph path: use
graphql-schema + apollo-server to define/run subgraphs, then use graphql-operations for smoke tests, then use the rover skill to compose or fetch supergraph.graphql.
- Use this
apollo-router skill to validate readiness (validation/checklist.md) and walk through runtime startup inputs.
Default endpoint remains http://localhost:4000 when using standard Router listen defaults.
If the user asks for executable shell commands, provide them on request. Otherwise keep Quick Start guidance skill-oriented.
Running Modes
| Mode |
Command |
Use Case |
| Local schema |
router --supergraph ./schema.graphql |
Development, CI/CD |
| GraphOS managed |
APOLLO_KEY=... APOLLO_GRAPH_REF=my-graph@prod router |
Production with auto-updates |
| Development |
router --dev --supergraph ./schema.graphql |
Local development |
| Hot reload |
router --hot-reload --supergraph ./schema.graphql |
Schema changes without restart |
Environment Variables
| Variable |
Description |
APOLLO_KEY |
API key for GraphOS |
APOLLO_GRAPH_REF |
Graph reference (graph-id@variant) |
APOLLO_ROUTER_CONFIG_PATH |
Path to router.yaml |
APOLLO_ROUTER_SUPERGRAPH_PATH |
Path to supergraph schema |
APOLLO_ROUTER_LOG |
Log level (off, error, warn, info, debug, trace) |
APOLLO_ROUTER_LISTEN_ADDRESS |
Override listen address |
Reference Files
- Configuration — YAML configuration reference
- Headers — Header propagation and manipulation
- Plugins — Rhai scripts and coprocessors
- Telemetry — Tracing, metrics, and logging
- Connectors — Router v2 connectors configuration
- Response Caching — Entity/root-field caching, invalidation, and observability (v2 only)
- Troubleshooting — Common issues and solutions
- Divergence Map — v1 ↔ v2 config differences
- Validation Checklist — Post-generation checks
CLI Reference
router [OPTIONS]
Options:
-s, --supergraph <PATH> Path to supergraph schema file
-c, --config <PATH> Path to router.yaml configuration
--dev Enable development mode
--hot-reload Watch for schema changes
--log <LEVEL> Log level (default: info)
--listen <ADDRESS> Override listen address
-V, --version Print version
-h, --help Print help
Ground Rules
- ALWAYS determine the target Router version (v1 or v2) before generating config
- DEFAULT to v2 for new projects
- ALWAYS include a comment block at top of generated config stating the target version
- ALWAYS use
--dev mode for local development (enables introspection and sandbox)
- ALWAYS disable introspection, sandbox, and homepage in production
- PREFER GraphOS managed mode for production (automatic updates, metrics)
- USE
--hot-reload for local development with file-based schemas
- NEVER expose
APOLLO_KEY in logs or version control
- USE environment variables (
${env.VAR}) for all secrets and sensitive config
- PREFER YAML configuration over command-line arguments for complex setups
- TEST configuration changes locally before deploying to production
- WARN if user enables
allow_any_origin or wildcard CORS in production
- RECOMMEND
router config upgrade router.yaml for v1 → v2 migration instead of regenerating from scratch
- MUST run
validation/checklist.md after every router config generation or edit
- MUST run
router config validate <file> when Router CLI is available
- MUST report when CLI validation could not run (for example, Router binary missing)
- MUST append a brief conditional handoff when runtime prerequisites are missing or unknown
- MUST make this handoff skill-first and avoid raw shell commands unless the user explicitly requests commands
- MUST keep Quick Start guidance skill-first and command-free unless the user explicitly requests commands
- MUST state that Rover is required only for the local supergraph path; GraphOS-managed runtime does not require local Rover composition
- USE
max_depth: 50 as the default starting point, not 15 (too aggressive) or 100 (too permissive)
- RECOMMEND
warn_only: true for initial limits rollout to observe real traffic before enforcing
- ONLY offer Response Caching when
ROUTER_VERSION=v2 (requires v2.6.0+)
- ALWAYS use
${env.*} for Redis URLs, passwords, and invalidation shared keys
- NEVER enable
response_cache.debug: true in production config
- RECOMMEND combining Cache-Control headers (passive TTL) with @cacheTag (active invalidation) for production
- ALWAYS ask which fields return user-specific data before generating response cache config — never assume all data is safe to cache as shared
- ALWAYS configure
private_id for subgraphs that serve user-specific data, and ensure those subgraphs return Cache-Control: private (via @cacheControl(scope: PRIVATE) in Apollo Server, or by setting the header directly in other frameworks)
- NEVER generate response cache config without addressing private data — if the user says "no user-specific data", confirm explicitly before proceeding
- ALWAYS bind the invalidation endpoint to
127.0.0.1, NEVER 0.0.0.0 in production
- NEVER conflate APQ with persisted-query safelisting — APQ (
apq) is a bandwidth optimization with no security value; safelisting (persisted_queries.safelist) is the operation allowlist. If a user asks to "lock down which queries can run", point them to safelisting, not APQ
- ALWAYS disable APQ (
apq.enabled: false) when enabling persisted_queries.safelist — they are mutually exclusive
- RECOMMEND starting persisted queries in audit mode (
log_unknown: true) to confirm all clients are registered before turning on safelist.enabled
- STATE that persisted-query safelisting requires a GraphOS-connected router (PQL fetched via
APOLLO_KEY + APOLLO_GRAPH_REF, or local_manifests for offline licenses)
- USE
persisted_queries (GA, v1.32.0+ and all v2), NOT preview_persisted_queries (v1.25.0–v1.32.0)
- TREAT global
authorization.require_authentication and declarative directives as different layers: the former gates the whole request, the latter (@authenticated / @requiresScopes / @policy) does field- and type-level filtering
- STATE that declarative authorization directives require a GraphOS-connected router (v1.29.1+; Developer/Standard plans need v2.6.0+) and a claims source (JWT auth or a coprocessor populating
apollo::authentication::jwt_claims)
- NOTE that authorization directives are ENABLED BY DEFAULT —
authorization.directives.enabled: false only turns them off; never imply config is required to "turn them on"
- STATE that
@policy additionally requires a Rhai script or coprocessor at the Supergraph stage to evaluate apollo::authorization::required_policies
- PLACE authorization directives in subgraph schemas, NEVER in
router.yaml — router config only enables/disables the feature
- RECOMMEND committing
router.yaml to version control and running router config validate in CI on every PR, with all secrets referenced via ${env.*} and injected at deploy time
- NEVER commit secrets (
APOLLO_KEY, JWKS/Redis URLs, invalidation keys) to the config file; the committed router.yaml must be safe to share with anyone holding repo access
1---2name: apollo-router3description: Version-aware guide for configuring and running Apollo Router for federated GraphQL supergraphs. Generates correct YAML for both Router v1.x and v2.x. Use this skill when: (1) setting up Apollo Router to run a supergraph, (2) configuring routing, headers, or CORS, (3) implementing custom plugins (Rhai scripts or coprocessors), (4) configuring telemetry (tracing, metrics, logging), (5) troubleshooting Router performance or connectivity issues, (6) securing the graph with JWT, declarative field-level authorization directives, or persisted-query safelisting, (7) managing router.yaml as version-controlled config with CI/CD validation.4license: MIT5---6
7# Apollo Router Config Generator
8
9Apollo Router is a high-performance graph router written in Rust for running Apollo Federation 2 supergraphs. It sits in front of your subgraphs and handles query planning, execution, and response composition.
10
11**This skill generates version-correct configuration.** Router v1 and v2 have incompatible config schemas in several critical sections (CORS, JWT auth, connectors). Always determine the target version before generating any config.
12
13## Step 1: Version Selection
14
15Ask the user **before generating any config**:
16
17```
18Which Apollo Router version are you targeting?
19
20 [1] Router v2.x (recommended — current LTS, required for Connectors)
21 [2] Router v1.x (legacy — end-of-support announced, security patches only)
22 [3] Not sure — help me decide
23```
24
25If the user picks **[3]**, display:
26
27```
28Quick guide:
29
30 • Pick v2 if: you're starting fresh, using Apollo Connectors for REST APIs,
31 or want backpressure-based overload protection.
32 • Pick v1 if: you have an existing deployment and haven't migrated yet.
33 Note: Apollo ended active support for v1.x. The v2.10 LTS (Dec 2025)
34 is the current baseline. Migration is strongly recommended.
35
36 Tip: If you have an existing router.yaml, you can auto-migrate it:
37 router config upgrade router.yaml
38```
39
40Store the selection as `ROUTER_VERSION=v1|v2` to gate all subsequent template generation.
41
42## Step 2: Environment Selection
43
44Ask: **Production** or **Development**?
45
46- **Production**: security-hardened defaults (introspection off, sandbox off, homepage off, subgraph errors hidden, auth required, health check on)
47- **Development**: open defaults (introspection on, sandbox on, errors exposed, text logging)
48
49Load the appropriate base template from:
50- `templates/{version}/production.yaml`
51- `templates/{version}/development.yaml`
52
53## Step 3: Feature Selection
54
55Ask which features to include:
56
57- [ ] JWT Authentication
58- [ ] Declarative Authorization (field-level `@authenticated` / `@requiresScopes` / `@policy` directives — requires GraphOS + request claims)
59- [ ] CORS (almost always yes for browser clients)
60- [ ] Operation Limits
61- [ ] Traffic Shaping / Rate Limiting
62- [ ] Telemetry (Prometheus, OTLP tracing, JSON logging)
63- [ ] APQ (Automatic Persisted Queries — performance/bandwidth only, NOT a security control)
64- [ ] Persisted Query Safelisting (GraphOS PQL operation allowlist — a security control; distinct from APQ)
65- [ ] Connectors (REST API integration — Router v2 only; GA key is `connectors`, early v2 preview key was `preview_connectors`)
66- [ ] Subscriptions
67- [ ] Header Propagation
68- [ ] Response Caching (entity + root field caching with Redis — Router v2 only, v2.6.0+)
69
70## Step 4: Gather Parameters
71
72For each selected feature, collect required values.
73
74- Use section templates from `templates/{version}/sections/` for `auth`, `cors`, `headers`, `limits`, `telemetry`, and `traffic-shaping`.
75- For Connectors in v2, use `templates/v2/sections/connectors.yaml` as the source.
76- For APQ and subscriptions, copy the snippet from the selected base template (`templates/{version}/production.yaml` or `templates/{version}/development.yaml`) or from references.
77- Only offer Connectors when `ROUTER_VERSION=v2`.
78
79### CORS
80- List of allowed origins (never use `"*"` for production)
81
82### JWT Authentication
83- JWKS URL
84- Issuer(s) — note: v1 uses singular `issuer`, v2 uses plural `issuers` array
85
86### Declarative Authorization (field-level)
87
88> Field- and type-level access control enforced **in the router**, via the `@authenticated`, `@requiresScopes`, and `@policy` directives applied in subgraph schemas. This is the layer that the global `authorization.require_authentication` gate cannot express. It is a **GraphOS feature** (Enterprise; Developer/Standard plans require Router v2.6.0+) and requires a router connected to GraphOS. Directives are **enabled by default** — config only turns them *off*.
89
90Confirm prerequisites before recommending these:
91
92- **Router connected to GraphOS** (Router v1.29.1+; Developer/Standard plans need v2.6.0+).
93- **A claims source.** Directives evaluate the claims at the `apollo::authentication::jwt_claims` context key. Populate it via JWT authentication (configure that feature too) **or** a coprocessor that injects claims.
94- **`@policy` additionally requires a Supergraph plugin** (Rhai script or coprocessor) to evaluate each policy — the router extracts required policies into `apollo::authorization::required_policies` but does not decide them itself.
95
96Ask:
97- **Which fields/types need protection, and at what level?** (`@authenticated` = any valid identity; `@requiresScopes` = specific scopes; `@policy` = custom logic.)
98- **Where do scopes/claims come from?** (JWT claims vs. coprocessor-injected.)
99
100The directives live in the **subgraph schemas**, not in `router.yaml`. The router config only enables/disables the feature and (for `@policy`) wires the evaluating plugin. See `references/configuration.md` → Authorization.
101
102### Persisted Query Safelisting (GraphOS PQL)
103
104> **Not the same as APQ.** APQ (`apq`) is a runtime bandwidth optimization that caches *any* operation a client sends — it provides **no** security. Safelisting uses a GraphOS-managed **Persisted Query List (PQL)** that clients register at build time; the router then **rejects operations not on the list**. This is the "persisted query safelisting" security control. It is a **GraphOS feature** requiring a router connected to GraphOS (`APOLLO_KEY` + `APOLLO_GRAPH_REF`).
105
106Pick a **security level** (increasing restrictiveness):
107
108| Level | Config | Behavior |
109|-------|--------|----------|
110| Audit (recommended first) | `persisted_queries.log_unknown: true` | Logs unregistered operations; rejects nothing. Use to confirm all clients are registered before enforcing. |
111| Safelist | `safelist.enabled: true` | Rejects operations not in the PQL. IDs *and* full strings both accepted if registered. |
112| Safelist, IDs only | `safelist.enabled: true` + `require_id: true` | Rejects unregistered operations **and** any freeform operation string, even if the string is registered. |
113
114Then gather:
115- **Is the router GraphOS-connected?** Safelisting needs the PQL fetched from GraphOS (or `local_manifests` for offline licenses).
116- **Have clients published their operations to the PQL** (via `rover persisted-queries publish` in their CI/CD)? If not, start in audit mode.
117- When enabling `safelist`, **APQ must be disabled** (`apq.enabled: false`) — they are mutually exclusive.
118
119Config key history: GA `persisted_queries` since v1.32.0 (was `preview_persisted_queries` in v1.25.0–v1.32.0); GA in all v2. See `references/configuration.md` → Persisted Query Safelisting.
120
121### Connectors (v2 only)
122- Subgraph name and source name (used as `connectors.sources.<subgraph>.<source>`)
123- Optional `$config` values for connector runtime configuration
124- If migrating old v2 preview config, rename `preview_connectors` to `connectors`
125
126### Operation Limits
127Present the tuning guidance:
128
129```
130Operation depth limit controls how deeply nested a query can be.
131
132 Router default: 100 (permissive — allows very deep queries)
133 Recommended starting point: 50
134
135 Lower values (15–25) are more secure but will reject legitimate queries
136 in schemas with deep entity relationships or nested fragments.
137 Higher values (75–100) are safer for compatibility but offer less
138 protection against depth-based abuse.
139
140 Tip: Run your router in warn_only mode first to see what depths your
141 real traffic actually uses, then tighten:
142 limits:
143 warn_only: true
144
145What max_depth would you like? [default: 50]
146```
147
148The same principle applies to `max_height`, `max_aliases`, and `max_root_fields`.
149
150### Telemetry
151- OTEL collector endpoint (default: `http://otel-collector:4317`)
152- Prometheus listen port (default: `9090`)
153- Trace sampling rate (default: `0.1` = 10%)
154
155### Traffic Shaping
156- Client-facing rate limit capacity (default: 1000 req/s)
157- Router timeout (default: 60s)
158- Subgraph timeout (default: 30s)
159
160### Response Caching (v2 only, v2.6.0+)
161
162> **Security: data leakage risk.** Before generating any response cache config, you MUST ask the user which types and fields return user-specific data. Cached data defaults to shared — subgraph responses without `Cache-Control: private` are visible to all users. User-specific subgraphs must return `Cache-Control: private` and have `private_id` configured on the router.
163
164- Ask: **Which subgraphs serve user-specific data?** (e.g., accounts, profiles, carts)
165- Ask: **How do you identify users?** (JWT `sub` claim, session token, API key)
166- Redis URL (default: `redis://localhost:6379`)
167- Default TTL (default: `5m`)
168- Enable active invalidation? If yes: invalidation listen address and shared key
169- Use section template: `templates/v2/sections/response-caching.yaml`
170- For security requirements, schema directives, and advanced config: `references/response-caching.md` (start with the Security section)
171
172## Step 5: Generate Config
173
1741. Load the correct version template from `templates/{version}/`
1752. Assemble section templates for supported sectioned features, then merge base-template snippets for APQ/subscriptions as needed
1763. Inject user-provided parameters
1774. Add a comment block at the top stating the target version
178
179## Step 6: Validate
180
181Run the [post-generation checklist](validation/checklist.md):
182
183- [ ] All env vars referenced in config are documented
184- [ ] CORS origins don't include wildcards (production)
185- [ ] Rate limiting is on `router:` (client-facing), not only `all:` (subgraph)
186- [ ] JWT uses `issuers` (v2) not `issuer` (v1), or vice versa
187- [ ] If production: introspection=false, sandbox=false, subgraph_errors=false
188- [ ] Health check is enabled
189- [ ] Homepage is disabled (production)
190- [ ] Run: `router config validate <file>` if Router binary is available
191
192## Required Validation Gate (always run)
193
194After generating or editing any `router.yaml`, you MUST:
195
1961. Run `validation/checklist.md` and report pass/fail for each checklist item.
1972. Run `router config validate <path-to-router.yaml>` if Router CLI is available.
1983. If Router CLI is unavailable, state that explicitly and still complete the checklist.
1994. Do not present the configuration as final until validation is completed.
200
201## Configuration as Code (git + CI/CD)
202
203`router.yaml` is the router's contract with every request — treat it like application code, not an ops afterthought. Whenever you generate or edit config, steer the user toward this workflow:
204
205- **Commit `router.yaml` to version control.** It should live in git alongside the service, with changes reviewed via pull request. This gives you history, blame, and rollback for the most safety-critical file in the API layer.
206- **Never commit secrets.** Keep `APOLLO_KEY`, JWKS URLs, Redis URLs, and invalidation keys out of the file — reference them with `${env.*}` expansion and inject at deploy time. The committed file should be safe to read by anyone with repo access.
207- **Validate in CI.** Run `router config validate router.yaml` on every PR so a malformed or version-mismatched config fails the build before it ships. Pin the Router version used in CI to the version you deploy.
208- **Pair config changes with schema checks.** Schema changes flow through `rover subgraph check` / `rover subgraph publish` (the `rover` skill); config changes flow through this validate-in-CI gate. Both gate the same deploy.
209- **Promote the same file across environments.** Differences between dev and prod should be expressed through env vars, not divergent committed files, so what you reviewed is what runs.
210
211A minimal CI step (provide actual commands only if asked):
212
213```yaml
214# Validate router config on every pull request
215- run: router config validate router.yaml
216```
217
218## Step 7: Conditional Next Steps Handoff
219
220After answering any Apollo Router request (config generation, edits, validation, or general Router guidance), decide whether the user already has runnable prerequisites:
221
222- GraphOS-managed path: `APOLLO_KEY` + `APOLLO_GRAPH_REF`, or
223- Local path: a composed `supergraph.graphql` plus reachable subgraphs
224
225If prerequisites are already present, do not add extra handoff text.
226
227If prerequisites are missing or unknown, end with a concise **Next steps** handoff (1-3 lines max) that is skill-first and command-free:
228
2291. Suggest the `rover` skill to compose or fetch the supergraph schema.
2302. Suggest continuing with `apollo-router` once the supergraph is ready to validate and run with the generated config.
2313. If subgraphs are missing, suggest `apollo-server`, `graphql-schema`, and `graphql-operations` skills to scaffold and test.
232
233Do not include raw shell commands in this handoff unless the user explicitly asks for commands.
234
235## Quick Start (skill-first)
236
2371. Use this `apollo-router` skill to generate or refine `router.yaml` for your environment.
2382. Choose a runtime path:
239 - GraphOS-managed path: provide `APOLLO_KEY` and `APOLLO_GRAPH_REF` (no local supergraph composition required).
240 - Local supergraph path: use `graphql-schema` + `apollo-server` to define/run subgraphs, then use `graphql-operations` for smoke tests, then use the `rover` skill to compose or fetch `supergraph.graphql`.
2413. Use this `apollo-router` skill to validate readiness (`validation/checklist.md`) and walk through runtime startup inputs.
242
243Default endpoint remains `http://localhost:4000` when using standard Router listen defaults.
244
245If the user asks for executable shell commands, provide them on request. Otherwise keep Quick Start guidance skill-oriented.
246
247## Running Modes
248
249| Mode | Command | Use Case |
250|------|---------|----------|
251| Local schema | `router --supergraph ./schema.graphql` | Development, CI/CD |
252| GraphOS managed | `APOLLO_KEY=... APOLLO_GRAPH_REF=my-graph@prod router` | Production with auto-updates |
253| Development | `router --dev --supergraph ./schema.graphql` | Local development |
254| Hot reload | `router --hot-reload --supergraph ./schema.graphql` | Schema changes without restart |
255
256## Environment Variables
257
258| Variable | Description |
259|----------|-------------|
260| `APOLLO_KEY` | API key for GraphOS |
261| `APOLLO_GRAPH_REF` | Graph reference (`graph-id@variant`) |
262| `APOLLO_ROUTER_CONFIG_PATH` | Path to `router.yaml` |
263| `APOLLO_ROUTER_SUPERGRAPH_PATH` | Path to supergraph schema |
264| `APOLLO_ROUTER_LOG` | Log level (off, error, warn, info, debug, trace) |
265| `APOLLO_ROUTER_LISTEN_ADDRESS` | Override listen address |
266
267## Reference Files
268
269- [Configuration](references/configuration.md) — YAML configuration reference
270- [Headers](references/headers.md) — Header propagation and manipulation
271- [Plugins](references/plugins.md) — Rhai scripts and coprocessors
272- [Telemetry](references/telemetry.md) — Tracing, metrics, and logging
273- [Connectors](references/connectors.md) — Router v2 connectors configuration
274- [Response Caching](references/response-caching.md) — Entity/root-field caching, invalidation, and observability (v2 only)
275- [Troubleshooting](references/troubleshooting.md) — Common issues and solutions
276- [Divergence Map](divergence-map.md) — v1 ↔ v2 config differences
277- [Validation Checklist](validation/checklist.md) — Post-generation checks
278
279## CLI Reference
280
281```
282router [OPTIONS]
283
284Options:
285 -s, --supergraph <PATH> Path to supergraph schema file
286 -c, --config <PATH> Path to router.yaml configuration
287 --dev Enable development mode
288 --hot-reload Watch for schema changes
289 --log <LEVEL> Log level (default: info)
290 --listen <ADDRESS> Override listen address
291 -V, --version Print version
292 -h, --help Print help
293```
294
295## Ground Rules
296
297- ALWAYS determine the target Router version (v1 or v2) before generating config
298- DEFAULT to v2 for new projects
299- ALWAYS include a comment block at top of generated config stating the target version
300- ALWAYS use `--dev` mode for local development (enables introspection and sandbox)
301- ALWAYS disable introspection, sandbox, and homepage in production
302- PREFER GraphOS managed mode for production (automatic updates, metrics)
303- USE `--hot-reload` for local development with file-based schemas
304- NEVER expose `APOLLO_KEY` in logs or version control
305- USE environment variables (`${env.VAR}`) for all secrets and sensitive config
306- PREFER YAML configuration over command-line arguments for complex setups
307- TEST configuration changes locally before deploying to production
308- WARN if user enables `allow_any_origin` or wildcard CORS in production
309- RECOMMEND `router config upgrade router.yaml` for v1 → v2 migration instead of regenerating from scratch
310- MUST run `validation/checklist.md` after every router config generation or edit
311- MUST run `router config validate <file>` when Router CLI is available
312- MUST report when CLI validation could not run (for example, Router binary missing)
313- MUST append a brief conditional handoff when runtime prerequisites are missing or unknown
314- MUST make this handoff skill-first and avoid raw shell commands unless the user explicitly requests commands
315- MUST keep Quick Start guidance skill-first and command-free unless the user explicitly requests commands
316- MUST state that Rover is required only for the local supergraph path; GraphOS-managed runtime does not require local Rover composition
317- USE `max_depth: 50` as the default starting point, not 15 (too aggressive) or 100 (too permissive)
318- RECOMMEND `warn_only: true` for initial limits rollout to observe real traffic before enforcing
319- ONLY offer Response Caching when `ROUTER_VERSION=v2` (requires v2.6.0+)
320- ALWAYS use `${env.*}` for Redis URLs, passwords, and invalidation shared keys
321- NEVER enable `response_cache.debug: true` in production config
322- RECOMMEND combining Cache-Control headers (passive TTL) with @cacheTag (active invalidation) for production
323- ALWAYS ask which fields return user-specific data before generating response cache config — never assume all data is safe to cache as shared
324- ALWAYS configure `private_id` for subgraphs that serve user-specific data, and ensure those subgraphs return `Cache-Control: private` (via `@cacheControl(scope: PRIVATE)` in Apollo Server, or by setting the header directly in other frameworks)
325- NEVER generate response cache config without addressing private data — if the user says "no user-specific data", confirm explicitly before proceeding
326- ALWAYS bind the invalidation endpoint to `127.0.0.1`, NEVER `0.0.0.0` in production
327- NEVER conflate APQ with persisted-query safelisting — APQ (`apq`) is a bandwidth optimization with no security value; safelisting (`persisted_queries.safelist`) is the operation allowlist. If a user asks to "lock down which queries can run", point them to safelisting, not APQ
328- ALWAYS disable APQ (`apq.enabled: false`) when enabling `persisted_queries.safelist` — they are mutually exclusive
329- RECOMMEND starting persisted queries in audit mode (`log_unknown: true`) to confirm all clients are registered before turning on `safelist.enabled`
330- STATE that persisted-query safelisting requires a GraphOS-connected router (PQL fetched via `APOLLO_KEY` + `APOLLO_GRAPH_REF`, or `local_manifests` for offline licenses)
331- USE `persisted_queries` (GA, v1.32.0+ and all v2), NOT `preview_persisted_queries` (v1.25.0–v1.32.0)
332- TREAT global `authorization.require_authentication` and declarative directives as different layers: the former gates the whole request, the latter (`@authenticated` / `@requiresScopes` / `@policy`) does field- and type-level filtering
333- STATE that declarative authorization directives require a GraphOS-connected router (v1.29.1+; Developer/Standard plans need v2.6.0+) and a claims source (JWT auth or a coprocessor populating `apollo::authentication::jwt_claims`)
334- NOTE that authorization directives are ENABLED BY DEFAULT — `authorization.directives.enabled: false` only turns them off; never imply config is required to "turn them on"
335- STATE that `@policy` additionally requires a Rhai script or coprocessor at the Supergraph stage to evaluate `apollo::authorization::required_policies`
336- PLACE authorization directives in subgraph schemas, NEVER in `router.yaml` — router config only enables/disables the feature
337- RECOMMEND committing `router.yaml` to version control and running `router config validate` in CI on every PR, with all secrets referenced via `${env.*}` and injected at deploy time
338- NEVER commit secrets (`APOLLO_KEY`, JWKS/Redis URLs, invalidation keys) to the config file; the committed `router.yaml` must be safe to share with anyone holding repo access