Request Scope Manager
Accessed via container.requestScopeManager. Controls REQUEST-scoped
bindings — one instance per asynchronous execution context.
REQUEST scope relies on Node.js AsyncLocalStorage and is only available in
Node.js environments.
RequestScopeManager
interface RequestScopeManager {
run<T>(fn: () => T | Promise<T>): Promise<T>
setStorage(storage: RequestScopeStorage): void
}
run
run<T>(fn: () => T | Promise<T>): Promise<Awaited<T>>
Starts a new request scope context and runs fn inside it. All
REQUEST-scoped bindings resolved within the callback — at any depth — return
the same instance for the duration of the call. Always returns a Promise
that resolves after all request-scoped instances are destroyed (including any
@PreDestroy hooks). Await it when cleanup ordering matters.
app.use(async (req, res, next) => {
await container.requestScopeManager.run(() => next())
})
Nested run() calls are not supported. Calling run() while a scope block is
already active throws ErrIllegalScopeState.
Accessing a REQUEST-scoped binding outside of a run() block throws
ErrOutOfScope.
setStorage
setStorage(storage: RequestScopeStorage): void
Replaces the underlying storage strategy. DiCaf uses AsyncLocalStorage
internally by default. Call setStorage() only when you need to substitute a
custom implementation — for example, in environments where AsyncLocalStorage
is not available or when using a test double.
setStorage() must be called before the first run(). Calling run() with no
storage set throws ErrNoRequestStorageSet.
RequestScopeStorage
interface RequestScopeStorage<T = any> {
run<R>(context: T, fn: () => R): R
getStore(): T | undefined
}
The storage contract used by the request scope. It mirrors the Node.js
AsyncLocalStorage
API, so an AsyncLocalStorage instance can be passed directly:
import { AsyncLocalStorage } from 'node:async_hooks'
container.requestScopeManager.setStorage(new AsyncLocalStorage())
See Request scope for full scope semantics and
@Lifetime to mark a binding as request-scoped.