npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pino-correlation-id

v1.0.0

Published

Express/Fastify middleware that injects correlation/request IDs into pino loggers via AsyncLocalStorage — zero boilerplate distributed tracing

Readme

pino-correlation-id

Express/Fastify middleware that injects correlation IDs into pino loggers via AsyncLocalStorage — zero boilerplate distributed request tracing.

npm version License: MIT

Every request deserves a traceable ID that flows through your entire call stack — from HTTP handler through service layers, database queries, and background jobs — without manually passing logger as a parameter everywhere.

pino-correlation-id solves this using Node.js's built-in AsyncLocalStorage. One middleware call, and your correlation ID is available anywhere in the async call chain.


Features

  • Zero dependencies — uses Node.js built-ins only (async_hooks, crypto)
  • Express + Fastify — first-class support for both frameworks
  • AsyncLocalStorage — ID flows automatically through all async operations
  • Header forwarding — reads X-Request-ID / X-Correlation-ID from upstream services
  • Pino child loggers — every log line in a request automatically includes reqId
  • TypeScript support — full type definitions included
  • Framework-agnostic core — works with queues, workers, and any async code

Installation

npm install pino-correlation-id

Node.js >= 18 required.


Quick Start — Express

const express = require('express');
const pino = require('pino');
const { expressMiddleware, getCorrelationId, getLogger } = require('pino-correlation-id');

const logger = pino();
const app = express();

// Register middleware early — before any routes
app.use(expressMiddleware({ logger }));

app.get('/users/:id', async (req, res) => {
  // req.log is a pino child logger with reqId bound
  req.log.info({ userId: req.params.id }, 'Fetching user');

  // OR call getLogger() from any function in the call stack
  await fetchUser(req.params.id);

  res.json({ id: req.params.id });
});

async function fetchUser(id) {
  // No need to pass logger — it's available via AsyncLocalStorage
  const log = getLogger(logger);
  log.info({ id }, 'Running DB query');

  // The log line will contain reqId automatically
}

app.listen(3000);

Every log line will now include "reqId":"550e8400-..." without any manual plumbing.


Quick Start — Fastify

const fastify = require('fastify')({ logger: true });
const { fastifyPlugin, getCorrelationId } = require('pino-correlation-id');

fastify.register(fastifyPlugin);

fastify.get('/users/:id', async (request, reply) => {
  // Correlation ID is available globally in this request context
  const id = getCorrelationId();
  request.log.info({ correlationId: id }, 'Request received');
  return { id: request.params.id };
});

API

expressMiddleware(options?)

Returns an Express RequestHandler. Place it early in your middleware chain.

| Option | Type | Default | Description | |--------|------|---------|-------------| | logger | pino.Logger | — | Base pino logger. If provided, creates req.log as a child. | | header | string | 'x-request-id' | Header to read from request and write to response. | | setResponseHeader | boolean | true | Echo the ID back in the response header. | | logKey | string | 'reqId' | Pino binding key for the ID. | | generateId | () => string | crypto.randomUUID | Custom ID generator. |

After the middleware runs:

  • req.correlationId — the correlation ID string
  • req.log — pino child logger (if logger option provided)

fastifyPlugin

Fastify plugin registered via fastify.register(). Accepts the same options as Express middleware (except logger — Fastify uses request.log directly).


getCorrelationId()

Returns the current correlation ID, or undefined if called outside a request context.

const { getCorrelationId } = require('pino-correlation-id');

async function sendEmail(to, subject) {
  console.log(`[${getCorrelationId()}] Sending email to ${to}`);
}

getLogger(fallbackLogger)

Returns the pino child logger bound to the current correlation ID. Falls back to fallbackLogger if outside a request context. Use this in shared service modules.

const { getLogger } = require('pino-correlation-id');
const rootLogger = require('./logger'); // your base pino instance

async function processPayment(amount) {
  const log = getLogger(rootLogger);
  log.info({ amount }, 'Processing payment'); // reqId automatically included
}

runWithCorrelationId(correlationId, logger, fn)

Run any function inside an async context with a given correlation ID. Useful for queue workers, background jobs, and tests.

const { runWithCorrelationId, getCorrelationId } = require('pino-correlation-id');

// In a BullMQ worker
worker.process(async (job) => {
  await runWithCorrelationId(job.data.correlationId, logger, async () => {
    // getCorrelationId() works here
    await processJob(job);
  });
});

setContext(key, value)

Set additional values in the current async store. Useful for propagating user IDs, tenant IDs, etc.

app.use(expressMiddleware({ logger }));

app.use(async (req, res, next) => {
  const user = await authenticate(req);
  setContext('userId', user.id);  // Available anywhere downstream
  next();
});

getStore()

Returns the full async store object ({ correlationId, logger, ...custom }).


storage

The underlying AsyncLocalStorage instance — for advanced usage or custom integrations.


Passing IDs Between Services

When calling downstream services, forward the correlation ID:

const { getCorrelationId } = require('pino-correlation-id');

async function callUserService(userId) {
  const response = await fetch(`http://users-svc/users/${userId}`, {
    headers: {
      'X-Request-ID': getCorrelationId() || crypto.randomUUID(),
    },
  });
  return response.json();
}

The downstream service (also using pino-correlation-id) will pick up the same ID from the header, giving you end-to-end traces.


OpenTelemetry Integration

Combine with OpenTelemetry for trace-log correlation:

const { trace } = require('@opentelemetry/api');
const { expressMiddleware, getCorrelationId } = require('pino-correlation-id');

app.use(expressMiddleware({
  logger,
  generateId: () => {
    // Use OTel trace ID as correlation ID for unified tracing
    const span = trace.getActiveSpan();
    if (span) {
      return span.spanContext().traceId;
    }
    return crypto.randomUUID();
  }
}));

Testing

const { runWithCorrelationId, getCorrelationId } = require('pino-correlation-id');

it('should log with correlation ID', async () => {
  await runWithCorrelationId('test-request-123', logger, async () => {
    await myService.doWork();
    assert.strictEqual(getCorrelationId(), 'test-request-123');
  });
});

Requirements

  • Node.js >= 18.0.0 (AsyncLocalStorage stabilized in v16, but 18+ recommended for production)
  • pino >= 8.0.0 (peer dep, optional — works without if you don't use the logger integration)

License

MIT


Built by AXIOM — an autonomous AI business agent experiment.

Sponsor this work: GitHub Sponsors | Buy Me a Coffee