Skip to content

fix: standardize X-Request-ID propagation across middlewares and error responses - #1560

Open
Chinko21 wants to merge 2 commits into
LabsCrypt:mainfrom
Chinko21:fix/issue-1494-batch-11_20
Open

Chinko21 wants to merge 2 commits into
LabsCrypt:mainfrom
Chinko21:fix/issue-1494-batch-11_20

Conversation

@Chinko21

Copy link
Copy Markdown

Overview

X-Request-ID was only partially propagated: requestIdMiddleware set the header but was mounted after the global rate limiter, so any response short-circuited by earlier middleware (the rate limiter's 429, the CORS 403) came back without the header, and the JSON error bodies produced by sendApiError never carried the id at all — so a client could not quote the request id from a failed call. This PR makes the id available to every response and every error payload.

Related Issue

Changes

  • Mount requestIdMiddleware as the very first middleware in backend/src/app.ts (before globalRateLimiter), so even short-circuited responses carry X-Request-ID. Inbound values keep the existing length/charset sanitisation.
  • Extract the request-scoped AsyncLocalStorage into backend/src/lib/request-context.ts and expose getRequestId(); logger.ts re-exports it so existing importers are unaffected, and the winston JSON format keeps appending requestId.
  • backend/src/types/api-error.ts: sendApiError now attaches requestId to every error body from the request context.
  • backend/src/middleware/requestId.ts: also expose the id as req.id for downstream handlers.
  • backend/src/middleware/rate-limiter.middleware.ts: default 429 handler preserves the configured message and stamps requestId.
  • backend/src/app.ts: the CORS 403 body now includes requestId alongside the existing error string (existing consumers unaffected).
  • Extend backend/tests/requestId.test.ts and backend/tests/error.middleware.test.ts with propagation coverage, including a real-app supertest case for the pre-router CORS 403 and for a sendApiError 400.

Verification Results

$ npx vitest run --coverage.enabled=false tests/requestId.test.ts tests/error.middleware.test.ts \
    tests/cors.test.ts tests/cancel.controller.test.ts tests/security-headers.test.ts tests/sse.controller.test.ts

 ✓ tests/cancel.controller.test.ts (6 tests) 171ms
 ✓ tests/error.middleware.test.ts (8 tests) 840ms
 ✓ tests/sse.controller.test.ts (7 tests) 300ms
 ✓ tests/requestId.test.ts (11 tests) 470ms
 ✓ tests/cors.test.ts (1 test) 476ms
 ✓ tests/security-headers.test.ts (3 tests) 548ms

 Test Files  6 passed (6)
      Tests  36 passed (36)

Winston output from the same run shows the id is still appended:
  {"level":"info","message":"request received","method":"GET","path":"/",
   "requestId":"6c33055d-f163-44b4-b04f-3bc9adf0611c","timestamp":"..."}

$ npx tsc --noEmit
# no errors reported in any changed file.

Pre-existing, unrelated to this PR: tests/worker-correlation-id.test.ts fails on main with
`ReferenceError: requestContext is not defined` in src/workers/soroban-event-worker.ts:228
(the module uses requestContext without importing it). It fails identically before this change
and is left untouched.
Acceptance Criteria Status
Every response includes X-Request-ID header ✅ requestIdMiddleware is now the first middleware; supertest covers a normal response, a client-supplied id, a sendApiError 400 and a pre-router CORS 403.
Error responses include requestId property in JSON body ✅ sendApiError reads the id from the request context; the 429 handler and the CORS 403 body stamp it too — covered by tests/requestId.test.ts and tests/error.middleware.test.ts.
Winston logs automatically append requestId ✅ unchanged JSON log format reads the id from the shared request context; requestContext.run still wraps each request (verified in the captured log lines above).

Closes #1494

@Chinko21

Chinko21 commented Oct 6, 2026

Copy link
Copy Markdown
Author

@LabsCrypt this PR's CI is green now.

Before: Backend CI, Frontend CI, Backend Docker Image CI, Soroban Contracts CI, Ephemeral full-stack preview
After: all of the above pass.

Root cause & fix
The request-id / error-envelope changes were correct but the branch was behind main, so CI ran against a stale base. I brought the branch up to date with main (no changes to this PR's own implementation), and the backend + preview suites went green.

Head 1756553367 → fa8a8ecb55.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Backend] Standardize X-Request-ID header propagation across Express middlewares and error responses

1 participant