Event-Manager
@voltage/event-manager is a type-safe event system for NestJS. Events are defined as classes, so emitting an event with the wrong arguments is a compile error, not a runtime surprise.
Installation
Section titled “Installation”yarn add @voltage/event-managerBasic usage
Section titled “Basic usage”Import EventManagerModule once in your root application module. Place it first in the imports array — other modules may emit events during their own bootstrap, and the event manager must be initialized before them to catch those events:
import { EventManagerModule } from '@voltage/event-manager';
@Module({ imports: [ EventManagerModule.forRoot(), // ... other modules ],})export class AppModule {}Because the module is global, you do not need to import it in feature modules.
Defining events
Section titled “Defining events”An event is a plain class with constructor parameters:
export class AfterOrderCreateEvent { constructor( public readonly order: Order, public readonly user: User, ) {}}Emitting events
Section titled “Emitting events”Inject EventEmitter and call emit() with the event class and its constructor arguments:
import { Injectable } from '@nestjs/common';import { EventEmitter } from '@voltage/event-manager';
@Injectable()export class OrderService { constructor(private readonly emitter: EventEmitter) {}
async create(user: User, items: Item[]) { const order = await this.repository.save({ user, items }); await this.emitter.emit(AfterOrderCreateEvent, order, user); return order; }}The emitter instantiates the event class for you. TypeScript infers the argument types from the constructor signature — pass the wrong types and it won’t compile.
Listening to events
Section titled “Listening to events”Add @OnEvent() to any method in any provider, service, or module class:
import { Injectable } from '@nestjs/common';import { OnEvent } from '@voltage/event-manager';
@Injectable()export class NotificationService { @OnEvent(AfterOrderCreateEvent) protected async onOrderCreate(event: AfterOrderCreateEvent) { await this.sendConfirmation(event.user, event.order); }}Listeners are discovered automatically at application bootstrap — no manual registration required. The method can be public, protected, or private.
Module classes are valid listeners too, which is useful when a module needs to react to events from other modules without introducing a dedicated service:
import { Module } from '@nestjs/common';import { OnEvent } from '@voltage/event-manager';
@Module({ controllers: [AuthController],})export class AuthModule { constructor(private readonly url: URLGenerator) {}
@OnEvent(NavEvent) protected onNavEvent(event: NavEvent) { event.actions.push({ label: 'Logout', url: this.url.generate(AuthController, 'logout'), }); }}Handler execution
Section titled “Handler execution”Handlers execute sequentially in registration order, each awaited before the next runs. This means handlers can mutate the event object and later handlers will see those changes:
export class NavEvent { actions: NavAction[] = [];}
// Handler A runs first@OnEvent(NavEvent)protected onNav(event: NavEvent) { event.actions.push({ label: 'Profile', url: this.url.generate(ProfileController) });}
// Handler B sees Handler A's additions@OnEvent(NavEvent)protected onNavAuth(event: NavEvent) { event.actions.push({ label: 'Logout', url: this.url.generate(AuthController, 'logout') });}Handler return values are discarded. Mutation is the only way to communicate back through the event.
Error handling
Section titled “Error handling”emit() throws on the first handler error, stopping execution of subsequent handlers:
await this.emitter.emit(AfterOrderCreateEvent, order, user);softEmit() logs errors and continues to the next handler:
await this.emitter.softEmit(AfterOrderCreateEvent, order, user);Use softEmit() when one handler failing should not affect others. Use emit() when handler failures are critical and should propagate.
Lifecycle events
Section titled “Lifecycle events”EventManagerModule automatically emits two lifecycle events you can subscribe to:
import { ApplicationBootstrapEvent, ApplicationShutdownEvent } from '@voltage/event-manager';
@Injectable()export class CacheService { @OnEvent(ApplicationBootstrapEvent) protected async onBootstrap() { await this.warmUp(); }
@OnEvent(ApplicationShutdownEvent) protected async onShutdown() { await this.flush(); }}