Async-Context
@voltage/async-context wraps Node’s AsyncLocalStorage for NestJS. It lets you store and access per-request data anywhere in the call stack — guards, services, listeners — without passing it through every function parameter.
Installation
Section titled “Installation”yarn add @voltage/async-contextBasic usage
Section titled “Basic usage”1. Define a context class
Section titled “1. Define a context class”Extend AbstractAsyncContext and expose the data you need. The base class provides this.request and this.response (Express objects, both nullable):
import { AbstractAsyncContext } from '@voltage/async-context';
export class UserContext extends AbstractAsyncContext { get user() { return this.request?.user; }}For writable per-request state, declare regular properties:
export class TraceContext extends AbstractAsyncContext { traceId?: string; startedAt?: bigint;}2. Register the module
Section titled “2. Register the module”Call forRoot() once in your root application module. Place it early in the imports array so its middleware runs before other modules initialize:
import { AsyncContextModule } from '@voltage/async-context';
@Module({ imports: [ AsyncContextModule.forRoot(), // ... other modules ],})export class AppModule {}Call forFeature() in the module that owns each context class. The result must be both imported and exported so the context is injectable downstream:
import { AsyncContextModule } from '@voltage/async-context';
const context = AsyncContextModule.forFeature([UserContext]);
@Module({ imports: [context], exports: [context],})export class AuthModule {}3. Inject and use
Section titled “3. Inject and use”Inject the context class like any other provider:
@Injectable()export class OrderService { constructor(private readonly userContext: UserContext) {}
async create(items: Item[]) { const user = this.userContext.user; return this.repository.save({ user, items }); }}Writing is the same as reading — assign directly:
this.traceContext.traceId = generateId();How it works
Section titled “How it works”The injected context object is a Proxy. Every read and write is transparently forwarded to the actual per-request instance stored in AsyncLocalStorage. From the outside it behaves like a regular injectable — from the inside it always resolves to the correct instance for the current request.
Context instances are lazy: they are only created the first time they are accessed within a request.
Non-HTTP contexts
Section titled “Non-HTTP contexts”In background jobs or other non-HTTP contexts, this.request and this.response are null. Context classes that only use writable properties work fine in these contexts. Getter-based contexts that depend on this.request will return undefined — guard against this where needed.