Skip to content

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.

Terminal window
yarn add @voltage/async-context

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;
}

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 {}

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();

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.

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.