API Gateway Patterns skill for Codex
A thin edge that never turns into a monolith. Codex chooses between a plain proxy, a shared gateway, per-client BFFs and a GraphQL layer, then puts TLS, token validation, header stripping, rate limits and request IDs at the edge while keeping authorization and business rules in services. BFF aggregation calls upstreams in parallel with per-call timeouts and returns partial data when optional services fail. Gateway config changes are diffed and confirmed before they hit shared environments.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- api-gateway-patterns Installs to ~/.agents/skills/api-gateway-patterns/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install api-gateway-patterns The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project api-gateway-patterns Installs to .agents/skills/api-gateway-patterns/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/api-gateway-patterns.zipunzip -o api-gateway-patterns.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) api-gateway-patterns PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $api-gateway-patterns, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Designs the edge of a service-based system - single API gateway, backend-for-frontend (BFF) per client, or GraphQL layer - deciding what belongs at the gateway (TLS, token validation, rate limits, routing, request IDs, size limits) and what must stay in services (authorization, business rules), plus response aggregation with timeouts and partial failure, using Kong, Envoy, the Kubernetes Gateway API, AWS API Gateway or a custom BFF. Use when adding a gateway or BFF, offloading auth, combining several service calls for a client screen, or reviewing a gateway that has grown business logic.
Use it when
- Adding a gateway or backend-for-frontend in front of several services
- Offloading authentication, rate limiting or CORS to the edge
- A client screen needs data from several services in one response
- A gateway has accumulated business logic or fragile transformations
Use something else when
- You are configuring Amazon API Gateway resources specifically: use aws-api-gateway
- You are designing the API contract itself rather than the edge: use api-design-review
- You need per-client rate-limit algorithms and headers in depth: use api-rate-limiting
How to check the result
A forged identity header with another user's valid token reaches the backend only as the verified identity, bad tokens get 401 at the edge, and a slowed optional upstream yields a degraded response within budget.
The mistake it prevents
Forwarding client-supplied X-User-Id style headers unchanged, letting any caller impersonate another user once auth is offloaded to the gateway.
A well-designed edge gives clients one stable entry point, rejects bad traffic before it reaches services, and composes responses without becoming a second monolith. The tests: the gateway holds no business rules, a slow backend degrades one part of a response instead of the whole page, and services still enforce authorization on their own data.
Pick the edge shape
| Shape | Use when | Watch out for |
|---|---|---|
| Reverse proxy / load balancer only | One backend or a modular monolith | Nothing more needed; do not add a gateway for its own sake |
| Single API gateway | Several services, one public API, cross-cutting concerns to centralise | Becoming the place every team edits; keep it config, not code |
| BFF per client (web, mobile, partner) | Clients need different shapes, payload sizes or release cadence | Duplicated logic across BFFs; each BFF owned by its client team |
| GraphQL gateway / federation | Many clients composing data from many services flexibly | Query cost limits, N+1 to backends, schema ownership ($graphql-federation) |
| Service mesh | East-west (service-to-service) mTLS, retries, telemetry | Not a replacement for the north-south gateway |
Common combination: managed or Kubernetes gateway at the edge for TLS, auth and limits, then a thin BFF per client for aggregation.
What belongs where
| Concern | Gateway | Service |
|---|---|---|
| TLS termination, HTTP/2, compression | Yes | - |
| Token validation (signature, expiry, audience, issuer) | Yes | Re-validate, or trust only gateway-authenticated mTLS traffic |
| Authorization on resources (“may user 7 read order 812?”) | No | Yes, always |
Coarse access (route requires scope orders:read) | Yes | Optional second check |
| Rate limiting, quotas per API key or tenant | Yes | Service-level protection (concurrency limits) too |
| Request size limits, header limits, method allow-lists | Yes | - |
| Request ID / trace context injection | Yes | Propagate |
| CORS | Yes, in one place | - |
| Routing, canary weights, versioned paths | Yes | - |
| Response caching of public GETs | Yes, with correct Cache-Control | Set the headers |
| Validation of business input, pricing, workflow | No | Yes |
| Aggregating several services for one screen | BFF, not the shared gateway | - |
Auth offload done safely
- Clients send
Authorization: Bearer <JWT>(or a session cookie to a BFF, which holds tokens server-side). - The gateway validates signature against the issuer’s JWKS (cached),
exp,nbf,issandaud. Reject on failure with 401. - The gateway strips any incoming identity headers (
X-User-Id,X-Tenant-Id) from the client request, then sets them from verified claims. Otherwise a client can spoof identity by sending the header itself. - Services accept identity headers only from the gateway: enforce with mTLS or network policy, or forward the original JWT and have services verify it themselves.
- Services make the authorization decision on the resource.
For browser clients, prefer a BFF with an httpOnly, Secure, SameSite session cookie over storing tokens in JavaScript; the BFF attaches tokens server-side.
Configuration examples
Kubernetes Gateway API (portable across Envoy Gateway, Istio, NGINX Gateway Fabric, cloud implementations):
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: orders
spec:
parentRefs:
- name: public-gateway
hostnames: ["api.example.com"]
rules:
- matches:
- path: { type: PathPrefix, value: /v1/orders }
backendRefs:
- name: orders
port: 8080
weight: 90
- name: orders-canary
port: 8080
weight: 10
Auth, rate limiting and timeouts are implementation-specific policies attached to routes
(for example Envoy Gateway SecurityPolicy and BackendTrafficPolicy); read the chosen
implementation’s docs rather than assuming field names.
Kong declarative config (decK) for edge concerns:
_format_version: "3.0"
services:
- name: orders
url: http://orders.internal:8080
connect_timeout: 2000
read_timeout: 5000
routes:
- name: orders
paths: ["/v1/orders"]
strip_path: false
plugins:
- name: rate-limiting
config: { minute: 600, policy: local, limit_by: consumer }
- name: request-size-limiting
config: { allowed_payload_size: 2 }
- name: correlation-id
config: { header_name: X-Request-ID, generator: uuid, echo_downstream: true }
Applying gateway config to a shared or production environment changes live traffic:
show the diff (deck gateway diff, kubectl diff) and get explicit confirmation first.
Aggregation in a BFF
Call independent backends in parallel, give each a timeout below the client’s budget, and return partial data with an explicit marker instead of failing the whole screen.
// BFF handler for the order details screen (Node 20+, global fetch)
export async function orderScreen(orderId: string, auth: string) {
const call = (url: string, ms: number) =>
fetch(url, { headers: { authorization: auth }, signal: AbortSignal.timeout(ms) })
.then((r) => (r.ok ? r.json() : Promise.reject(new Error(`${url} -> ${r.status}`))));
const [order, shipment, recs] = await Promise.allSettled([
call(`http://orders/v1/orders/${orderId}`, 800), // required
call(`http://shipping/v1/shipments?order=${orderId}`, 600),
call(`http://recs/v1/for-order/${orderId}`, 300), // nice to have
]);
if (order.status === "rejected") throw new UpstreamError("orders", order.reason);
return {
order: order.value,
shipment: shipment.status === "fulfilled" ? shipment.value : null,
recommendations: recs.status === "fulfilled" ? recs.value : [],
degraded: [shipment, recs].some((r) => r.status === "rejected"),
};
}
Rules for aggregation:
- Classify each upstream as required or optional; only required failures fail the response.
- Timeouts: total client budget minus margin, then per-call budgets below it. No upstream call without a timeout.
- No retries at the BFF for non-idempotent calls; for idempotent GETs at most one retry within the budget.
- Avoid sequential dependent calls where a backend can return the needed data directly.
- Keep transformation to shaping and renaming; if a rule decides something about the business, it belongs in a service.
Workflow
- [ ] 1. Read AGENTS.md and existing ingress/gateway config; list clients and services
- [ ] 2. Choose the edge shape (table above); record an ADR if introducing a new layer
- [ ] 3. Assign concerns to gateway vs service (table above) and remove misplaced logic
- [ ] 4. Configure auth validation, header stripping, rate limits, size limits, request IDs
- [ ] 5. Build BFF aggregation with per-call timeouts and partial responses
- [ ] 6. Add metrics per route (rate, errors, p99) and upstream timeout counters
- [ ] 7. Diff and confirm before applying gateway config to shared environments
Gotchas
- The gateway as an ESB: transformation scripts, business validation and orchestration in Lua or policy XML nobody tests. Move logic into services or a BFF with tests.
- One shared BFF for all clients becomes a general-purpose gateway again. One BFF per client experience, owned by that client’s team.
- Gateway timeouts shorter than backend timeouts cause work to continue after the client gave up; align them so each layer’s timeout is shorter than its caller’s.
- Rate limiting per IP behind a CDN limits the CDN; key on API key, user or tenant, and configure trusted proxy headers correctly.
- Gateways that retry POSTs duplicate side effects. Retry only idempotent methods or requests with idempotency keys ($idempotency-keys).
- A single gateway is a single point of failure and a shared blast radius; run it replicated across zones and roll out config changes gradually.
Verification
- Send a request with a forged
X-User-Idheader and a valid token for another user: the backend must see the verified identity only. - Expired, wrong-audience and wrong-issuer tokens get 401 at the gateway.
- Slow one optional upstream (fault injection or a stub with delay): the BFF returns within
budget with
degraded: true. - Rate limit returns 429 with
Retry-Afterat the configured threshold for one key, and does not affect other keys.
Output contract
**Edge shape:** <gateway | gateway + BFFs | GraphQL> (ADR-00xx)
**Gateway concerns:** TLS, JWT validation (iss/aud), header stripping, rate limits <values>, size limit <value>, request IDs
**Moved out of gateway:** <logic moved to services/BFF>
**BFF endpoints:** <route -> upstreams (required/optional), budgets>
**Verification:** spoofed header test, token tests, degraded-mode test, 429 test What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "API Gateway Patterns"
short_description: "A thin edge that never turns into a monolith"
default_prompt: "$api-gateway-patterns Design a BFF for our mobile app that combines orders, shipping and recommendations" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.