Trust is earned, not given

A different perspective

2020-05-26 · Projects

Node.js, part 10: AsyncLocalStorage — request-scoped context without threading it through every call

Part 10from the Node.js series · 24 parts in all

A request arrives, and forty frames deeper a function wants the request id so it can log it. The alternatives are all unpleasant: pass it as a parameter through every layer, hang it off an implicit global (which is wrong the moment two requests overlap), or give up and log nothing. AsyncLocalStorage is the runtime feature that makes it correct.

One store, entered per request

const { AsyncLocalStorage } = require('async_hooks');
const { randomUUID } = require('crypto');

const store = new AsyncLocalStorage();

// Middleware: everything this request triggers runs inside this store,
// including promises and timers started later.
app.use((req, res, next) => {
  store.run({ requestId: randomUUID(), userId: req.user?.id }, next);
});

// Anywhere downstream, with nothing passed in:
function log(msg) {
  const ctx = store.getStore() || {};
  console.log(JSON.stringify({ requestId: ctx.requestId, msg }));
}

The magic is that the context follows the async chain, not the call stack. A setTimeout, a database callback, a promise three awaits later — all still see the store that was active when they were created. That is what makes it safe under concurrency, unlike a module-level variable.

Where it earns its keep

The costs, stated honestly

It is built on async_hooks, historically one of the more expensive corners of the runtime — fast enough now to use broadly, but not free, so measure if you are in the hundreds of thousands of requests per second. Two behaviours to internalise: the store is only visible to code that runs inside run() (a function called outside the chain sees nothing), and it is deliberately not a general-purpose state manager — React-like "context" is not what this is. Treat it as a logger/tracer channel that happens to be readable. Next: cancellation, the other thing Node lacked for a decade.