Appearance
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, Cookie, and X-CSRF-Token request headers are also removed because they authenticate the client to Gatelin, not to an internal service. 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. Cookies and CSRF headers are not forwarded.
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.