patterntypescriptTip
Request Context with AsyncLocalStorage: Pass request data without prop drilling
Viewed 0 times
async-local-storagerequest-contextthread-localtracingtenantnode
nodejs
Problem
Passing request context (user ID, trace ID, tenant ID) through function call chains requires threading parameters through every function signature. This couples business logic to HTTP infrastructure concerns.
Solution
Use Node.js AsyncLocalStorage to store request-scoped context. Populate it in a middleware at the start of each request. Access it anywhere in the call chain without parameters.
Why
AsyncLocalStorage provides a request-local store that travels with the async execution context. It eliminates parameter threading without global state, making code cleaner while maintaining request isolation.
Gotchas
- AsyncLocalStorage context does not propagate through setTimeout or unstructured callbacks unless you explicitly bind them — use AsyncResource if needed.
- Do not store large objects in the store — it is not a caching layer.
- The context is scoped to the async execution context; it is not available in synchronous code called from a different context.
Code Snippets
AsyncLocalStorage request context
import { AsyncLocalStorage } from 'async_hooks'
interface RequestContext {
requestId: string
userId?: string
tenantId?: string
}
export const requestContext = new AsyncLocalStorage<RequestContext>()
// Middleware
export function contextMiddleware(req: Request, res: Response, next: NextFunction) {
requestContext.run({
requestId: crypto.randomUUID(),
userId: req.user?.id,
tenantId: req.headers['x-tenant-id'] as string
}, next)
}
// Anywhere in the call chain — no parameters needed
export function getCurrentUserId(): string | undefined {
return requestContext.getStore()?.userId
}Revisions (0)
No revisions yet.