API Gateway Patterns Cheat Sheet
Architectural patterns for API gateways covering routing, rate limiting, authentication, and request aggregation.
Common Gateway Responsibilities
Cross-cutting concerns typically centralized in an API gateway.
- Routing- Directs requests to the correct backend service by path, host, or header
- Authentication/Authorization- Validates API keys, JWTs, or OAuth tokens before forwarding requests
- Rate limiting / throttling- Enforces per-client request quotas to protect backends
- Request/response transformation- Rewrites headers, bodies, or protocols (e.g. REST to gRPC)
- Observability- Centralizes access logs, tracing, and metrics for all API traffic
Kong Route + Rate Limiting Plugin
Declarative Kong config defining a service, route, and rate-limit plugin.
services: - name: users-service url: http://users.internal:8080 routes: - name: users-route paths: - /api/users plugins: - name: rate-limiting config: minute: 100 policy: local
JWT Validation at the Gateway
Example gateway-level auth config validating a bearer JWT before proxying.
plugins: - name: jwt config: claims_to_verify: - exp key_claim_name: iss secret_is_base64: false
API Gateway Design Patterns
Higher-level architectural patterns built on top of a gateway.
- BFF (Backend for Frontend)- A dedicated gateway per client type (web, mobile) tailored to its needs
- Aggregation- Gateway fans out to multiple services and composes a single response
- Circuit breaking- Gateway stops forwarding to a failing backend after a failure threshold, failing fast
- Canary/blue-green routing- Gateway shifts traffic percentages between backend versions
Advanced Gateway Concerns
Cross-cutting problems that show up once a gateway is handling real production traffic at scale.
- Idempotency keys- Gateway or backend deduplicates retried POST/PUT requests using a client-supplied key to avoid double side effects
- Response caching- Gateway caches GET responses by cache-control/vary headers to shed load from backends
- Protocol translation- Gateway exposes REST/JSON externally while transcoding to gRPC internally, or vice versa
- Header propagation- Forwards trace IDs (e.g. traceparent) and correlation IDs so requests are traceable across the fan-out
- Strangler fig migration- Gateway routes a slice of paths to a new service while legacy paths still hit the monolith, enabling incremental rewrites
- Gateway vs. mesh sidecar- Edge gateway handles north-south (client-to-cluster) traffic; a service mesh sidecar handles east-west (service-to-service) traffic — they solve different problems and are often layered together
- Schema/contract validation- Gateway rejects malformed requests against an OpenAPI/JSON-Schema spec before they reach a backend
Envoy Gateway Route with Retry Policy
Envoy route config applying exponential-backoff retries and a per-try timeout at the edge, distinct from application-level retries.
routes: - match: prefix: "/api/orders" route: cluster: orders-service timeout: 2s retry_policy: retry_on: "5xx,reset,connect-failure" num_retries: 3 per_try_timeout: 0.5s retry_back_off: base_interval: 0.1s max_interval: 1s
OAuth2 Token Introspection Plugin
Gateway validates opaque OAuth2 access tokens against an authorization server's introspection endpoint (RFC 7662) instead of a locally verifiable JWT.
plugins: - name: oauth2-introspection config: introspection_url: https://auth.example.com/oauth2/introspect client_id: gateway-client client_secret: ${OAUTH_INTROSPECT_SECRET} token_type_hint: access_token ttl: 30 # cache introspection result for 30s to cut latency cache_size: 10000 required_scopes: - orders:read
gRPC-JSON Transcoding at the Edge
Envoy filter that lets external REST/JSON clients call an internal gRPC service without either side knowing about the other's protocol.
http_filters: - name: envoy.filters.http.grpc_json_transcoder typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder proto_descriptor: "/etc/envoy/proto.pb" services: ["orders.v1.OrdersService"] print_options: add_whitespace: true always_print_primitive_fields: true
Response Aggregation (API Composition)
A gateway-layer BFF endpoint fans out to two backends in parallel and composes a single response, hiding the fan-out from the client.
// GET /bff/order-summary/:idapp.get('/bff/order-summary/:id', async (req, res) => { const { id } = req.params; try { const [order, shipment] = await Promise.all([ fetchWithTimeout(`http://orders-svc/orders/${id}`, 800), fetchWithTimeout(`http://shipping-svc/shipments?orderId=${id}`, 800), ]); res.json({ order, // degrade gracefully instead of failing the whole response shipment: shipment ?? { status: 'unavailable' }, }); } catch (err) { res.status(502).json({ error: 'upstream_unavailable' }); }});
Keep the gateway thin on business logic — it should handle cross-cutting concerns (auth, rate limiting, routing) only, otherwise it becomes a shared bottleneck that every team must coordinate through to ship.