There's a pattern I've seen in almost every Express codebase I've worked in. It starts small.
You need to log a correlation ID inside a service function. So you add req as a parameter. Then the function that calls it needs req. Then the one above that. Within a few weeks, half your codebase takes req as a parameter even though it only ever reads one field from it.
// This is where it starts
async function processOrder(orderId: string, req: Request) {
logger.info('Processing order', { correlationId: req.headers['x-correlation-id'] });
await notifyWarehouse(orderId, req); // now warehouse needs req too
await sendEmail(orderId, req); // and email
await auditLog('order.processed', req); // and audit
}
Every function signature is polluted. Tests need a mock req. Services are coupled to Express internals. It's a mess.
The fix is AsyncLocalStorage — a Node.js built-in (stable since v16) that lets you store data scoped to an async call chain and read it from anywhere inside that chain, without passing it through function parameters.
I wrapped it into 26 tests, full TypeScript, zero runtime dependencies. If you've been passing
GitHub: github.com/SaifuddinTipu/express-correlation-context
req through 6 layers of service functions, this is the fix. Drop a ⭐ if it's useful.
SOCIAL SHARE CARD GENERATOR