Working with Async Hooks

1. Creating Async Hooks (async_hooks.createHook)

Example

import async_hooks from "node:async_hooks";
const hook = async_hooks.createHook({
  init(asyncId, type, triggerAsyncId, resource) {},
  before(asyncId) {},
  after(asyncId) {},
  destroy(asyncId) {}
});
hook.enable();

2. Using init Callback

ParamDescription
asyncIdUnique async resource id
typee.g. TCPWRAP, Timeout, PROMISE
triggerAsyncIdParent context id
resourceThe actual resource

3. Using before and after Callbacks

HookFires
beforeJust before resource callback runs
afterRight after callback returns

4. Using destroy Callback

HookFires
destroyResource GC'd or manually destroyed
promiseResolveWhen a promise resolves

5. Getting Current Execution ID (executionAsyncId)

APIReturns
async_hooks.executionAsyncId()Current async context id
async_hooks.executionAsyncResource()Current resource

6. Getting Trigger Async ID (triggerAsyncId)

APIReturns
async_hooks.triggerAsyncId()Caller's async id

7. Using AsyncLocalStorage

Example: Per-request store

import { AsyncLocalStorage } from "node:async_hooks";
const als = new AsyncLocalStorage();
http.createServer((req, res) => {
  als.run({ requestId: crypto.randomUUID() }, () => handler(req, res));
}).listen(3000);
function log(msg) {
  const { requestId } = als.getStore() ?? {};
  console.log(requestId, msg);
}

8. Tracking Request Context

UseDetail
Trace IDsPass to logger / spans
User identityAvoid threading through every fn
Tenant infoMulti-tenant routing

9. Debugging Async Operations

ToolPurpose
--async-stack-traces (default)Cross-await stacks
Async hook tracingMap causal chains
OpenTelemetry contextDistributed traces

10. Understanding Performance Implications

ConcernDetail
Async hooks have overheadUse sparingly; AsyncLocalStorage is optimized path
Don't enable in productionUnless via AsyncLocalStorage
Avoid heavy work in callbacksRuns on every async transition