Skip to Content
Correlation IDGetting Started

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-id
yarn add @evanion/nestjs-correlation-id
pnpm add @evanion/nestjs-correlation-id

Register 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() returns undefined.
  • Nest runs middleware in the order configure() applies it, and the correlation context opens in CorrelationIdMiddleware. Apply your own middleware that reads the id in a later consumer.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-0042

The 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-4f1c9a under x-correlation-id, the lowercase name Node gives every incoming header
  • once for a request with no header, where the generator returns orders-0f3a2b so 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' .(); // -> undefined

The 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.

Last updated on