nestjs-request-context

Access the current HTTP request from any singleton NestJS service via AsyncLocalStorage.

Library
npm
v4.0.0
73stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
28/100Needs Attention
Development Activity0
Maintenance0
Community32
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
54/100Fair
Architecture65
Code Quality45
Innovation62
Learning Curve45

nestjs-request-context solves a specific NestJS pain point: reading data off the current HTTP request from a singleton (application-scoped) provider. NestJS’s own answer is a REQUEST-scoped provider, but that re-instantiates the provider and its whole dependency graph on every request, which gets expensive fast. This library instead ships a middleware, registered once via RequestContextModule, that wraps every incoming request in a Node AsyncLocalStorage.run() call, storing the req/res pair for the lifetime of that request’s async execution.

Any singleton service can then call the static RequestContext.currentContext accessor to read the request and response objects that triggered the current call stack, no matter how many layers of async calls separate it from the controller. The library has zero runtime dependencies, a single peerDependency on @nestjs/common, and a generic RequestContext<TRequest, TResponse> type so it works with typed Express or Fastify request/response shapes instead of only the default any.

What You Get

  • A RequestContextModule that wires the context-capturing middleware across every route with one import
  • A static RequestContext.currentContext accessor for reading the active req/res from any singleton-scoped service
  • Generic RequestContext<TRequest, TResponse> typing so req/res can be typed for Express, Fastify, or a custom adapter
  • Zero runtime dependencies beyond Node’s built-in async_hooks, with only @nestjs/common as a peerDependency

Common Use Cases

  • Logging services that need to tag lines with the current request ID without becoming REQUEST-scoped
  • Multi-tenant applications where a singleton service must resolve the current tenant from the request
  • Audit-trail services that stamp writes with the user and path of the request that triggered them
  • Correlating downstream calls or background work back to the HTTP request that originated them

Under The Hood

Architecture The library is deliberately small: a RequestContext model (src/request-context.model.ts) wraps a static AsyncLocalStorage instance and exposes a currentContext getter, a RequestContextMiddleware (src/request-context.middleware.ts) runs every request through cls.run(new RequestContext(req, res), next), and a RequestContextModule (src/request-context.module.ts) implements NestModule to register that middleware across every route in one configure() call. There are no additional layers, no internal services, and no data flow beyond storing and reading the req/res pair for the duration of a request’s async execution — the whole surface area is three files re-exported from a single index.ts.

Tech Stack Written in TypeScript and built with plain tsc, with a single peerDependency on @nestjs/common and zero runtime dependencies of its own; the actual context propagation rides on Node’s built-in async_hooks module rather than any third-party continuation-local-storage package. Tests run under Jest with ts-jest and exercise the module through a real NestJS app via @nestjs/testing and supertest. Linting uses tslint with tslint-config-prettier, and formatting is handled by Prettier.

Code Quality There is exactly one test file (test/index.spec.ts), which boots a small TestModule through Nest’s testing utilities and asserts on two sequential HTTP requests that the request ID differs while the singleton service’s own initialization count stays at one — a good smoke test for the core AsyncLocalStorage behavior, but there is no unit-level coverage of RequestContext or the middleware in isolation, and no CI workflow file was found in the repository. Naming is clear and the code is fully typed, though it relies on tslint, a linter that has been deprecated in favor of ESLint for TypeScript projects.

API Design The public surface is intentionally tiny and low-friction: importing RequestContextModule wires the middleware across the whole app with no manual provider registration, and any singleton service reads the current request with a single static property, RequestContext.currentContext. Generic typing (RequestContext<TRequest, TResponse>) lets consumers type req/res for their actual HTTP adapter. The trade-off for this simplicity is that the accessor is untyped by default (falls back to any) unless the consumer opts into the generics, and there’s no built-in way to scope or namespace multiple concurrent contexts beyond what AsyncLocalStorage itself provides.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search