Proxy (Request Forwarding)
Requests that match a registered route but are not handled by a /gatelin/* admin or session router are forwarded to the target microservice. Every request still goes through checkRoute first — including proxied ones.
Proxied HTTP and WebSocket handshakes are rate-limited to PROXY_RATE_LIMIT_MAX (default 200) per consumer per minute. Public routes fall back to the client IP. Login/refresh stay on a separate IP window (SESSION_RATE_LIMIT_MAX). Exceeded windows return 429.
Example
GET /api/users/123
Authorization: Bearer <access_token>With Traefik stripping the /api prefix, Gatelin sees /users/123 (or whatever pattern you registered).
Flow:
- Gatelin validates the route exists and matches the configured pattern
- Validates the JWT access token (if
protected: truefor the route) - Checks the consumer session and that a permission grants access
- Checks ACL route, operation, scope, condition, and field permissions
- Injects
x-consumer-user-id,x-consumer-name, and optionallyx-acl-conditionsandx-acl-fields - Streams the request to
{SERVER_SCHEME}{APP_NAME}-{serviceName}-{ENV_NAME}:{PORT}with the original method, path, query string, body, and safe client headers - Streams the microservice status, headers, and body back to the client
Transparent forwarding
Proxy bodies are not parsed or re-serialized by Gatelin. JSON, form data, multipart uploads, binary payloads, and streaming responses pass through as bytes. Upstream response headers such as Content-Type, Location, and Set-Cookie are preserved.
Hop-by-hop headers are removed in both directions. The client Authorization and X-CSRF-Token request headers are also removed because they authenticate the client to Gatelin, not to an internal service. The Cookie header is removed too, except for the cookie names registered in the service's forwardedCookies; Gatelin session cookies are never forwarded. Protected services receive trusted identity and ACL context through the injected x-consumer-* and x-acl-* headers.
Because proxy bodies remain streams, Gatelin does not rewrite JSON fields or filters for proxied routes. Services must enforce the x-acl-fields and x-acl-conditions context they receive on reads, writes, history, and schema output. Gatelin's own /gatelin/* APIs continue to parse JSON and apply field and condition filtering internally. See Integrating Gatelin — Enforcing ACL headers for the required upstream contract.
GraphQL over HTTP
Register /graphql (or any path your service uses) as a normal proxied route. Queries and mutations are ordinary POST/GET HTTP bodies and now pass through as bytes. Gatelin does not parse GraphQL documents or enforce field-level GraphQL ACL; services should honor x-acl-* if they need row or field rules.
GraphQL subscriptions are not JSON-over-HTTP. Use SSE or WebSocket for those transports, with the same route + JWT + ACL handshake.
Server-Sent Events
SSE is HTTP/1.1 (GET, Accept: text/event-stream or an upstream Content-Type: text/event-stream). The proxy streams chunks without buffering the full body.
The generic UPSTREAM_TIMEOUT_MS idle timeout is disabled for event streams so sparse heartbeats are not killed after 30s. Disconnect when the client or upstream closes the socket. Last-Event-ID is a normal request header and is forwarded.
Register the SSE path as a GET route. ACL is checked once on the GET; Gatelin does not inspect event payloads.
WebSockets
WebSocket Upgrade never enters Express. The HTTP server handles upgrade beside the REST pipeline:
- Match a registered GET route for the path
- Enforce CORS origin whitelist when
Originis present - Authenticate and authorize once (JWT + ACL)
- Apply the same consumer (or IP) rate limit as the HTTP proxy
- Inject
x-consumer-*/x-acl-*on the handshake only - Pipe sockets after
101 Switching Protocols
Browser WebSocket cannot set Authorization. Protected routes may pass the access token as ?access_token= or ?token=; Gatelin uses it for the handshake and strips it before contacting the upstream. Node and other clients may send Authorization: Bearer. CSRF headers are not forwarded; cookies follow the same forwardedCookies rule as HTTP.
After 101, frames are opaque. There is no per-message ACL.
Traefik must allow WebSockets on the route that strips /api and forwards to Gatelin (default in Traefik HTTP routers).
gRPC
Native gRPC (HTTP/2, trailers, application/grpc) is out of scope. Terminate gRPC on Traefik or Envoy on a separate listener. grpc-web over HTTP/1.1 may pass as opaque bytes, but Gatelin does not implement HTTP/2 or gRPC status trailers.
Resilience
Each proxied hop is one attempt. Streamed bodies cannot be replayed, so Gatelin does not retry, hedge, or trip a circuit on the data path. Unreachable upstreams return 503; an idle timeout returns 504.
Outbound HTTP and HTTPS use a shared keep-alive agent so sockets to internal services are reused. Cap concurrency with UPSTREAM_MAX_SOCKETS. Control-plane fetch() (password check, user search) already pools via Node/undici.
Idempotent GET retries, if you want them, belong on the client or Traefik — not in Gatelin.
Health Check
GET /gatelin/healthReturns liveness and database readiness. This endpoint bypasses checkRoute.