express-request-id
Express middleware that assigns a UUID to each incoming request and exposes it via the X-Request-Id header.
Repository Health
Technical Analysis
express-request-id is a minimal Express middleware that attaches a unique identifier to every incoming request. It generates a UUID v4 by default, storing it on req.id and mirroring it in the X-Request-Id response header, so downstream handlers, logs, and clients share a single correlation ID for the lifetime of a request.
If an inbound request already carries an X-Request-Id header, the middleware reuses that value instead of overwriting it, which lets upstream proxies, load balancers, or client SDKs set their own IDs and have them propagate through your Express app untouched. The header name, ID generator, and whether the header is written back at all are all configurable, making it easy to adapt to an existing tracing or logging convention.
What You Get
- A middleware factory that generates a UUID v4 for every request lacking one
- Automatic reuse of an existing X-Request-Id header instead of overwriting it
- A configurable header name via the headerName option
- A pluggable ID generator function to swap UUID v4 for your own scheme
- TypeScript type definitions augmenting Express’s Request type with an id property
Common Use Cases
- Correlating log lines across a single request’s lifecycle
- Propagating a trace/request ID to downstream services and clients
- Returning a request ID in API responses for support/debugging correlation
- Respecting client-supplied request IDs for distributed tracing
Under The Hood
Architecture The package is a single-file implementation (index.js) exporting a default factory function, requestID(), that closes over three options — generator, headerName, and setHeader — and returns a standard Express middleware signature (request, response, next). There are no internal layers or abstractions beyond this one closure: the middleware reads an inbound header via request.get(headerName), falls back to the configurable generator (default generateV4UUID, backed by the uuid package) when the header is absent, optionally writes the value back via response.set(headerName, id), and always assigns it to request.id before calling next(). Because the entire behavior lives in one function, there is nothing to break if the abstraction changes — the surface area is the options object itself, and consumers depend only on the request.id property and the response header it sets.
Tech Stack The runtime dependency surface is a single package, uuid (^9.0.0), used only for its v4 UUID generator. The package is published as pure ESM (type: module, exports: ./index.js) with hand-written TypeScript definitions in types.d.ts that augment Express’s global Request interface with an id: string property. There is no build step — the published files are the source files verbatim. Development tooling includes ava for the test runner, supertest and express as peer test dependencies, and xo (an opinionated ESLint preset) for linting, wired together via a single npm test script (xo && ava) and a GitHub Actions CI workflow referenced in the README badge.
Code Quality test.js exercises all five documented behaviors — UUID assignment, preserving an existing X-Request-Id header, the setHeader: false option, a custom headerName, and a custom generator function — using ava’s test() blocks driven through supertest against a real express() app, with an explicit errorHandler that fails the test on any middleware error rather than swallowing it. Naming is consistent and minimal, and xo enforces a strict lint profile on every test run. Type safety is provided via a hand-authored types.d.ts rather than compiled TypeScript, which covers the public API surface but isn’t verified against the implementation by a compiler.
API Design The public API is a single default export, requestID(options?), invoked once as Express middleware (app.use(requestID())) with zero required configuration — sensible defaults (UUID v4, X-Request-Id, setHeader: true) cover the common case in one line. The three options (generator, headerName, setHeader) are independent and orthogonal, so adopters can override only what they need — for example swapping in a different ID generator without touching header behavior. The README documents the full option set with types and defaults inline, and the bundled types.d.ts gives TypeScript consumers request.id typing out of the box without any extra setup.