vert.x Base Module Headers (For vert.x based modules all headers from incoming request should exist in newly created request)  

vert.x Base Module Headers (For vert.x based modules all headers from incoming request should exist in newly created request)  

Context

Vert.x-based FOLIO modules receive inbound HTTP requests via the folio-module-sidecar. By the time the module's handler executes, the RoutingContext already carries a full set of Okapi headers that the sidecar validated and, where needed, enriched — X-Okapi-Tenant, X-Okapi-Token, X-Okapi-User-Id, X-Okapi-Request-Id, X-System-Token, and others.

When the same module needs to make an outgoing call to another module, it creates a new vert.x WebClient or HttpClient request. Because this is a new request object, none of the incoming headers are copied automatically. Developers must explicitly propagate the relevant headers from the incoming RoutingContext to every newly created outbound request.

Omitting this step is a recurring source of failures in Eureka environments where the sidecar enforces strict header requirements on egress traffic.


How the Sidecar Uses These Headers

When a vert.x module's outbound request reaches the sidecar's egress filter chain, the sidecar reads the following from the request:

  • X-Okapi-Tenant — required to identify the tenant context, look up entitlements, resolve the target module's location, and select the correct Keycloak realm. If this header is absent, the sidecar cannot route the request and returns a 400.

  • X-Okapi-Token / Authorization — the user's bearer token. If absent, the sidecar falls back to fetching a system user token. This fallback works only when the target module's endpoint is configured to accept system user credentials. Endpoints that require the original user's identity will fail or return incorrect authorization results.

  • X-Okapi-User-Id — used by downstream modules and audit logging to identify who initiated the action. Without it, audit records and user-context-aware logic in downstream modules receive no user identity.

  • X-Okapi-Request-Id — used for distributed tracing. The sidecar appends a new segment to the chain. If the header is absent on an outgoing request, the trace chain breaks and the downstream call appears unrelated to its parent request in logs.

  • X-Okapi-Permissions — the granted permissions for the current user and endpoint. Downstream modules that read this header to adjust behavior will see an empty value if it is not propagated.

The sidecar's RequestForwardingService propagates all incoming request headers when it forwards traffic — but this applies only to requests that arrive at the sidecar. A request created entirely within the module and sent via vert.x WebClient is handed directly to the sidecar's egress port and does not benefit from this automatic copy; the module must do it explicitly.


Why Missing Headers Cause Failures

Failing to propagate Okapi headers from the incoming context to a newly created request leads to several categories of problem:

  • Tenant routing failure. The sidecar uses X-Okapi-Tenant to resolve where to forward the egress request. Without it the sidecar cannot match an entitlement and returns an error.

  • Silent authorization downgrade. When X-Okapi-Token is missing, the sidecar substitutes a system user token. This may succeed at the transport level but the downstream module's action is attributed to the system user rather than the actual end user. This corrupts audit logs and can silently bypass user-specific permissions.

  • Broken trace chains. Distributed traces spanning multiple modules become disconnected in logs when X-Okapi-Request-Id is not propagated.

  • Downstream logic errors. Modules that inspect X-Okapi-User-Id or X-Okapi-Permissions to make decisions (display, filtering, auditing) receive empty values and may produce wrong results or errors.

  • Hard-to-diagnose intermittent failures. Because missing headers interact differently depending on whether the endpoint requires a user token or accepts a system token, failures appear inconsistent and are difficult to trace without understanding that the root cause is missing header propagation.


Recommended Approach

When building an outbound HTTP call from inside a vert.x handler, copy all Okapi headers from the incoming RoutingContext to the new request. A safe pattern is to iterate the headers of rc.request() and copy every X-Okapi-* header, plus Authorization:

// Incoming RoutingContext from the handler method public Future<SomeResponse> callOtherModule(RoutingContext rc) { var incomingHeaders = rc.request().headers(); return webClient.get(targetModulePort, targetModuleHost, "/some/path") .putHeader(OkapiHeaders.TENANT, incomingHeaders.get(OkapiHeaders.TENANT)) .putHeader(OkapiHeaders.TOKEN, incomingHeaders.get(OkapiHeaders.TOKEN)) .putHeader(OkapiHeaders.USER_ID, incomingHeaders.get(OkapiHeaders.USER_ID)) .putHeader(OkapiHeaders.REQUEST_ID, incomingHeaders.get(OkapiHeaders.REQUEST_ID)) .send() .map(response -> parseResponse(response)); }

A more robust version avoids null values by copying all headers whose name starts with the X-Okapi- prefix, plus Authorization:

private void propagateOkapiHeaders(HttpRequest<?> request, MultiMap incoming) { incoming.entries().stream() .filter(e -> e.getKey().toLowerCase().startsWith("x-okapi-") || e.getKey().equalsIgnoreCase(OkapiHeaders.AUTHORIZATION)) .forEach(e -> request.putHeader(e.getKey(), e.getValue())); }

Apply the same pattern regardless of whether the target module is on the same sidecar network segment or reached via the gateway fallback.

Minimum set of headers to propagate on every outbound call:

Header

Why it is required

Header

Why it is required

X-Okapi-Tenant

Tenant identification and sidecar routing

X-Okapi-Token or Authorization

User identity and authorization at the target

X-Okapi-User-Id

User context for audit and permission-aware logic

X-Okapi-Request-Id

Distributed trace chain continuity

Optional but recommended:

Header

Why

Header

Why

X-Okapi-Permissions

Downstream modules that alter behavior based on granted permissions


What Not to Do

Do not create a new outbound WebClient request without headers and rely on the sidecar to reconstruct the tenant or user context. The sidecar can only inject what it already knows: it will add a system token when the user token is absent, but it cannot invent a X-Okapi-User-Id or restore a broken request-id chain.

Do not copy headers selectively based on assumptions about which downstream module checks them. The correct approach is to propagate the full Okapi header set from the incoming request and let the sidecar and the target module discard what they do not need.