@vizydrop/correlation-id
v6.0.2
Published
Vizydrop correlation id helper based on async hooks
Readme
Correlation Id
Correlation ID propagation across async contexts using AsyncLocalStorage. Provides middleware for Express/Koa and enhancers for HTTP clients (got, request, http-proxy) to automatically attach correlation IDs and baggage headers to all outbound requests.
Written in TypeScript (versions > 6.0).
Installation
yarn add @vizydrop/correlation-idHow to use
Create correlation id instance
const {createCorrelationId} = require('@vizydrop/correlation-id');
const correlationId = createCorrelationId();Integrate with @vizydrop/logger
const {createLogger} = require(`@vizydrop/logger`);
const logger = createLogger({
correlationId: {
enabled: true,
getCorrelationId: () => correlationId.correlator.getId(),
emptyValue: 'nocorrelation',
},
baggage: {
enabled: true,
getBaggage: () => correlationId.correlator.getParsedBaggage(),
},
});Register middleware
Support for koa and express:
// express
app.use(correlationId.expressMiddleware);
// koa
app.use(correlationId.koaMiddleware);Enhance got
Each request will automatically contain the correlation ID and baggage headers:
import got from 'got';
const correlatedGot = correlationId.enhanceGot(got);
correlatedGot.get('http://anotherservice:10020/data');Enhance request
const request = require('request');
const correlatedRequest = correlationId.enhanceRequest(request);
correlatedRequest.get('http://anotherservice:10020/data');Enhance http-proxy
Each proxied request will contain the correlation ID and baggage headers:
import httpProxy from 'http-proxy';
const proxy = httpProxy.createProxyServer({target: 'http://anotherservice:10020/'});
correlationId.enhanceHttpProxy(proxy);Run background jobs
Jobs usually do not go through express/koa middleware so correlation ID should be generated manually:
function jobTask() { /* ... */ }
function runJob() {
correlationId.correlator.withId(correlationId.correlator.generateId(), () => {
jobTask();
});
}Baggage Propagation
Baggage allows attaching key-value metadata to a request context and propagating it across services via the Baggage HTTP header (W3C standard).
Automatic propagation
Both Express and Koa middleware propagate inbound Baggage headers — if a request arrives with a Baggage header, it is captured and forwarded on outbound requests (via enhanceGot, enhanceRequest, enhanceHttpProxy).
However, only the Express middleware supports creating baggage from request data via the extractBaggage callback. Koa middleware only forwards existing baggage headers.
Custom baggage extraction (Express only)
Extract baggage from request data (e.g., JWT tokens, custom headers):
const correlationId = createCorrelationId({
extractBaggage: (req) => ({
userId: req.headers['x-user-id'],
accountId: req.headers['x-account-id'],
}),
});Manual baggage management
correlationId.correlator.withId('my-id', () => {
// Set baggage
correlationId.correlator.setBaggage('key1=value1,key2=value2');
// Get raw baggage string
correlationId.correlator.getBaggage(); // 'key1=value1,key2=value2'
// Get parsed baggage as object
correlationId.correlator.getParsedBaggage(); // {key1: 'value1', key2: 'value2'}
});Custom baggage header name
const correlationId = createCorrelationId({
baggageHeaderName: 'x-custom-baggage',
});Settings
Custom settings can be passed as an object to createCorrelationId:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| generateDefaultId | () => string | uuid.v4() | Function that returns a new correlation ID |
| httpHeaderParamName | string | 'x-correlationid' | HTTP header name for correlation ID |
| baggageHeaderName | string | 'Baggage' | HTTP header name for baggage propagation |
| extractBaggage | (req) => Record \| null | — | Custom function to extract baggage from request |
API Reference
createCorrelationId(opts?)
Returns a CorrelationIdApi object with:
| Property | Description |
|----------|-------------|
| correlator | Core correlator instance |
| expressMiddleware | Express middleware |
| koaMiddleware | Koa middleware |
| enhanceGot(got) | Returns enhanced got instance with correlation headers |
| enhanceRequest(request) | Returns enhanced request instance with correlation headers |
| enhanceHttpProxy(proxy) | Registers proxyReq listener that adds correlation headers |
correlator
| Method | Description |
|--------|-------------|
| getId() | Returns current correlation ID (or undefined outside context) |
| withId(id, fn) | Run function within a correlation ID context |
| withIdAndReturn(id, fn) | Same as withId but returns the function's return value |
| generateId() | Generate a new correlation ID |
| setBaggage(value) | Set baggage string for current context |
| getBaggage() | Get raw baggage string |
| getParsedBaggage() | Get baggage as parsed key-value object |
Development
yarn install # Install dependencies
yarn build # Compile TypeScript to dist/
yarn test # Run tests
yarn lint # Run ESLintKnown issues
- Does not work well with
bluebird.promisifyAll. Alternative solution is to explicitly promisify using native promise:
const redis = require('redis');
const util = require('util');
const client = redis.createClient();
client.setAsync = util.promisify(client.set).bind(client);
client.getAsync = util.promisify(client.get).bind(client);- Does not work well with
mongoosecallbacks. Alternative solution is to use promisified functions:
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
EntityModel.find({name: 'name'}).then((value) => {
// do something
});