Gateway Routing
Kong Gateway routes requests to your backend services based on OpenAPI specs. Routes are generated from your code, so you rarely need to configure routing manually. The OpenAPI document is the single source of truth: an operation that is not in it is not reachable through the gateway.
How routing works
When you run npx tsdevstack sync, the framework:
- Reads OpenAPI specs from each service (
apps/{service}/docs/openapi.json) - Groups operations by security type (public, JWT, partner) based on decorators
- Generates
kong.tsdevstack.ymlwith one exact route per path (see Exact routes) - Merges with
kong.user.yml(your customizations) - Writes the final
kong.ymlwith resolved secrets
Local architecture
Locally, all requests flow through Kong at http://localhost:8000:
Note: Route prefixes use the short service name (e.g., /auth/, /offers/) not the full package name.
In cloud deployments, Kong runs as a managed service (Cloud Run, etc.) with different networking.
Route types
Routes are grouped into three types based on your OpenAPI decorators.
Two-layer authentication
Authentication happens at two independent layers:
Important: Kong routing is determined by @ApiBearerAuth() and @PartnerApi(). The @Public() decorator only affects the backend AuthGuard, not Kong routing. For fully public endpoints, you need both: omit @ApiBearerAuth() (for Kong) AND add @Public() (for AuthGuard).
Public routes
No JWT required at Kong. Routes without @ApiBearerAuth():
Accessible at: POST http://localhost:8000/auth/v1/auth/login
JWT-authenticated routes
Require valid JWT token. Routes with @ApiBearerAuth():
Accessible at: GET http://localhost:8000/auth/v1/user/account
Kong validates the JWT using the OIDC plugin before forwarding to the service. Invalid tokens get a 401 response from Kong.
Partner API routes
Require API key. Routes with @PartnerApi():
Each @PartnerApi() path is exposed with an /api prefix in front of its OpenAPI path:
Only the paths and methods marked @PartnerApi() get a partner route. An endpoint without it is not reachable with an API key at all: /api/... for it returns 404 from Kong. Kong removes the /api prefix before forwarding (framework plugin tsdevstack-api-prefix), so the service receives /offers/v1/plans, the path it serves.
If a service's OpenAPI paths do not start with its route prefix, generate-kong prints a warning; the partner URL is still /api plus the OpenAPI path.
Where the keys come from
Partner keys are runtime data, not config: admins create, limit, rotate and revoke them through the auth-service admin API, and the gateway checks each key against Redis on every request. Changes apply on the next request, without regenerating or redeploying the gateway. Requests with a missing, unknown, revoked or expired key get 401 from Kong, over-limit requests 429, and none of them reach your service. See API Keys.
Dual-access routes
Routes can support both JWT and API key:
Creates two Kong routes:
/service/v1/data(JWT via Authorization header)/api/service/v1/data(API key via x-api-key header)
Exact routes
Every generated route (public, JWT and partner) matches exactly what your OpenAPI document declares, and nothing else:
- Path: an anchored regex.
/offers/v1/plansmatches only/offers/v1/plans, not/offers/v1/plans/anything/extra. - Path parameters:
{id}in the OpenAPI path matches exactly one non-empty segment. - Literal beats parameter: when
/plans/featuredand/plans/{id}both exist, a request to/plans/featuredgoes to the literal route. Routes with more literal segments win. - Methods: only the methods the OpenAPI document lists for that path, plus
OPTIONSso the gateway's CORS plugin can answer browser preflights.HEADis not added toGETroutes. - One route per path: all methods of a path share one route, named
{service}-{public|jwt|partner}-{path}(for exampleoffers-service-jwt-offers-v1-user-assign-plan).
Anything else gets 404 from Kong (no Route matched with those values) before it reaches your service:
Hiding endpoints from OpenAPI is fine for things that must not be public anyway (scheduled job endpoints, health checks, metrics). Anything that should be callable through the gateway must be in the OpenAPI document.
NestJS wildcard routes (@Get('files/*splat')) and optional segments (@Get('items{/:id}')) appear in the OpenAPI document as a single-segment parameter or as one of the variants. Kong then routes only that: one segment for the wildcard, one variant for the optional route. Declare the variants you need as separate routes.
Versioned routes
Use @Version() to version your endpoints:
Access at /service/v1/profile and /service/v2/profile.
Regenerating routes
After changing decorators, regenerate and restart:
This regenerates kong.yml and restarts all containers automatically.
Custom Kong configuration
Add custom routes or plugins in kong.user.yml. This file is merged with framework-generated routes:
Troubleshooting
Route returns 404
If Kong returns 404 (no Route matched with those values) for a route that should exist:
- Check the request against the exact route rules: no trailing slash, no extra segments, and a method the endpoint declares
- Check the operation is in the OpenAPI spec:
apps/{service}/docs/openapi.json(not hidden with@ApiExcludeEndpoint()) - For partner requests (
/api/...), check the handler has@PartnerApi() - Run
npx tsdevstack syncto regenerate configs and restart containers, then check the route is inkong.yml
Route returns 502
Kong can reach the route but the service isn't responding:
- Run
npx tsdevstack syncto regenerate and restart - Verify the service is healthy:
curl http://localhost:<port>/health
JWT validation fails
If authenticated routes return 401 even with a valid token:
- Check the JWKS endpoint is accessible:
curl http://localhost:8000/auth/.well-known/jwks.json - Verify the token hasn't expired
Routes not updating
After changing decorators:
Public route requires auth
If a route you expect to be public requires JWT:
- Make sure you omitted
@ApiBearerAuth()(for Kong) - Add
@Public()(for AuthGuard) - Run
npx tsdevstack sync