Getting started
@evanion/nestjs-correlation-id 2.1.0 and its patch releases up to 2.1.1, the version on npm. Values on this page were produced by running the source at 77650ee, a later commit than the 2.1.0 tag, and are not re-executed.
By the end of this page every request to your NestJS application carries a
correlation id, and a handler reads it from CorrelationService.
Before you install nestjs-correlation-id
@evanion/nestjs-correlation-id needs:
- A NestJS 12 application, meaning
@nestjs/common^12.0.0, on the Express or the Fastify adapter. - Node 20 or newer. The package ships only ES modules, so the minimum depends on
your project’s module format:
- An ES module project imports it on any Node 20.
- A CommonJS project loads it through
require(), which needs Node 20.19, 22.12 or newer. NestJS 12 needs the same versions in a CommonJS project.
Install nestjs-correlation-id
npm install @evanion/nestjs-correlation-idyarn add @evanion/nestjs-correlation-idpnpm add @evanion/nestjs-correlation-idRegister CorrelationModule and the middleware
CorrelationModule.forRoot() from @evanion/nestjs-correlation-id provides
CorrelationService to every module, and CorrelationIdMiddleware opens a
correlation context around each request. Register both in your root module:
import { MiddlewareConsumer, , NestModule } from '@nestjs/common';
import {
,
,
} from '@evanion/nestjs-correlation-id';
@({
: [.()],
})
export class implements NestModule {
(: MiddlewareConsumer) {
.().('*');
}
}forRoot() with no argument is the whole minimum configuration. The middleware
then reads X-Correlation-Id and generates a missing id with randomUUID from
node:crypto. It reuses an incoming id only when the id passes
DEFAULT_CORRELATION_ID_VALIDATOR, which accepts 1 to 128 characters from the
ASCII letters, the digits, _, ., : and -. forRoot() also takes three
optional fields, header, generator and validate, that replace those
defaults.
forRoot()registers a global module, so call it once, in the root module.forRoutes('*')covers every route. On a route the middleware skips,getCorrelationId()returnsundefined.- Nest runs middleware in the order
configure()applies it, and the correlation context opens inCorrelationIdMiddleware. Apply your own middleware that reads the id in a laterconsumer.apply()call, or later in the same call’s argument list.
Read the request’s id in a handler
Inject CorrelationService from @evanion/nestjs-correlation-id and call
getCorrelationId(). OrdersController in Baize’s orders service returns the
id with the order and writes both to its log line:
import { Controller, Get, Logger, Param } from '@nestjs/common';
import { CorrelationService } from '@evanion/nestjs-correlation-id';
@Controller('orders')
export class OrdersController {
private readonly logger = new Logger(OrdersController.name);
constructor(private readonly correlation: CorrelationService) {}
@Get(':order')
find(@Param('order') order: string) {
const correlationId = this.correlation.getCorrelationId();
this.logger.log({ order, correlationId });
return { order, correlationId };
}
}getCorrelationId() is synchronous, and nothing passes the id to find(): the
controller reads it from the context the middleware opened.
Check that the caller’s id comes back
To see @evanion/nestjs-correlation-id reuse a caller’s id, add
controllers: [OrdersController] to the @Module() of AppModule and start
the application. The request below assumes it listens on port 3000, the port
nest new sets up. Send a request with an id:
curl -i -H 'X-Correlation-Id: storefront-4f1c9a' http://localhost:3000/orders/order-2026-0042The response carries the header X-Correlation-Id: storefront-4f1c9a and the
body {"order":"order-2026-0042","correlationId":"storefront-4f1c9a"}. Leave
the header off and both carry a fresh universally unique identifier (UUID).
The code below checks the same result without a server, in a form a unit test
can use. It builds CorrelationService and CorrelationIdMiddleware by hand,
the two objects Nest builds in an application, and calls the middleware with
stub request and response objects:
- once for a request that carries
storefront-4f1c9aunderx-correlation-id, the lowercase name Node gives every incoming header - once for a request with no header, where the generator returns
orders-0f3a2bso that the result is predictable
import {
,
,
} from '@evanion/nestjs-correlation-id';
const = { : 'X-Correlation-Id', : () => 'orders-0f3a2b' };
const = new ();
// The middleware opens its contexts on the service it is given.
const = new (, );
// The middleware reads the request's headers and writes the id with setHeader.
// It leaves a response header that something earlier already set, so it asks
// getHeader first, and this stub answers that nothing did. `as never` lets the
// stubs stand in for Node's full IncomingMessage and ServerResponse.
const = { : () => , : () => };
const = (: <string, string>) => {
let : string | undefined;
.({ } as never, as never, () => {
= .();
});
return ;
};
({ 'x-correlation-id': 'storefront-4f1c9a' }); // -> 'storefront-4f1c9a'
({}); // -> 'orders-0f3a2b'
.(); // -> undefinedThe configuration sets no validate, so the middleware applied
DEFAULT_CORRELATION_ID_VALIDATOR, and storefront-4f1c9a passed it.
Send the id on to the stock service
Every request to your application now carries an id from
@evanion/nestjs-correlation-id, and a handler reads it.
Forward the id to the next service passes withCorrelation()
to HttpModule, so Baize’s stock service receives the id the orders service
was called with.