exp-correlator: Async Context and Express Correlation ID
raw JSON →exp-correlator is a Node.js library designed to manage and propagate a correlation ID across asynchronous operations and within Express.js applications. It leverages Node.js's `async_hooks` to automatically track a unique identifier through various async calls, eliminating the need to explicitly pass the ID between functions. The current stable version is 1.0.0, and while a specific release cadence isn't stated, active GitHub workflows suggest ongoing maintenance. Its primary differentiator is the use of `async_hooks` for transparent context propagation, integrating seamlessly as an Express middleware or through a standalone async handler, and providing direct examples for integration with logging libraries like Pino and HTTP clients like exp-fetch.
Common errors
error TypeError: Cannot read properties of undefined (reading 'use') ↓
const app = express(); is executed before attempting to apply exp-correlator's middleware. error correlationId is consistently 'undefined' in logs or when retrieved by getId() ↓
middleware is correctly applied to your Express app or that attachCorrelationIdHandler wraps the entry point of your async operation. Review complex async patterns to ensure context is not inadvertently broken. error Error: `exp-correlator` requires Node.js version `14.17` or higher. ↓
14.17 or newer to ensure compatibility and correct functionality. Warnings
gotcha Reliance on `async_hooks` for context propagation means `exp-correlator` requires Node.js `v14.17` or higher. Running on older Node.js versions will result in runtime errors. ↓
gotcha If `getId()` returns `undefined`, it indicates that the code is executing outside an `exp-correlator` context initialized by either the `middleware` or `attachCorrelationIdHandler`. This often happens with event emitters or certain callback patterns that inadvertently break the async flow. ↓
gotcha While powerful for transparent context management, `async_hooks` can introduce a slight performance overhead in extremely high-throughput applications or those with deeply nested and frequently invoked async operations. Monitor CPU and memory usage under load. ↓
Install
npm install exp-correlator yarn add exp-correlator pnpm add exp-correlator Imports
- middleware wrong
import { middleware } from 'exp-correlator';correctconst { middleware } = require('exp-correlator'); - getId wrong
import { getId } from 'exp-correlator';correctconst { getId } = require('exp-correlator'); - attachCorrelationIdHandler wrong
import { attachCorrelationIdHandler } from 'exp-correlator';correctconst { attachCorrelationIdHandler } = require('exp-correlator');
Quickstart
const express = require('express');
const { middleware, getId } = require('exp-correlator');
const app = express();
const port = 3000;
const logMessage = async (msg) => {
const correlationId = getId();
console.log({ timestamp: new Date().toISOString(), correlationId, msg });
};
// Simulate an external call that needs the correlation ID
const callToExternalSystem = async () => {
const correlationId = getId();
console.log({ timestamp: new Date().toISOString(), context: 'external-call', correlationId, message: 'Making external request' });
// In a real app, you'd add correlationId to headers, e.g., 'correlation-id'
await new Promise(resolve => setTimeout(resolve, 100)); // Simulate network delay
};
app.use(middleware); // Apply the correlation ID middleware to all incoming requests
app.get('/', async (req, res) => {
const initialCorrelationId = getId();
await logMessage("Request received for /");
await callToExternalSystem();
await logMessage("After external system call");
res.json({ message: 'Hello World!', correlationId: initialCorrelationId });
});
app.get('/test', async (req, res) => {
const initialCorrelationId = getId();
await logMessage("Request received for /test");
res.json({ message: 'Test endpoint', correlationId: initialCorrelationId });
});
app.listen(port, () => {
console.log(`Server listening on http://localhost:${port}`);
console.log('Try: curl -H "correlation-id: my-custom-id" http://localhost:3000');
console.log('Or: curl http://localhost:3000/test');
});