Events
Event Emitter package (@nestjs/event-emitter) provides a simple observer implementation, allowing you to subscribe and listen for various events that occur in your application. Events serve as a great way to decouple various aspects of your application, since a single event can have multiple listeners that do not depend on each other.
EventEmitterModule internally uses the eventemitter2 package.
Getting started#
First install the required package:
$ npm i --save @nestjs/event-emitter
Once the installation is complete, import the EventEmitterModule into the root AppModule and run the forRoot() static method as shown below:
import { Module } from '@nestjs/common';
import { EventEmitterModule } from '@nestjs/event-emitter';
@Module({
imports: [
EventEmitterModule.forRoot()
],
})
export class AppModule {}
The .forRoot() call initializes the event emitter and registers any declarative event listeners that exist within your app. Registration occurs when the onApplicationBootstrap lifecycle hook occurs, ensuring that all modules have loaded and declared any scheduled jobs.
To configure the underlying EventEmitter instance, pass the configuration object to the .forRoot() method, as follows:
EventEmitterModule.forRoot({
// set this to `true` to use wildcards
wildcard: false,
// the delimiter used to segment namespaces
delimiter: '.',
// set this to `true` if you want to emit the newListener event
newListener: false,
// set this to `true` if you want to emit the removeListener event
removeListener: false,
// the maximum amount of listeners that can be assigned to an event
maxListeners: 10,
// show event name in memory leak message when more than maximum amount of listeners is assigned
verboseMemoryLeak: false,
// disable throwing uncaughtException if an error event is emitted and it has no listeners
ignoreErrors: false,
});
Dispatching events#
To dispatch (i.e., fire) an event, first inject EventEmitter2 using standard constructor injection:
constructor(private eventEmitter: EventEmitter2) {}
Hint Import theEventEmitter2from the@nestjs/event-emitterpackage.
Then use it in a class as follows:
this.eventEmitter.emit(
'order.created',
new OrderCreatedEvent({
orderId: 1,
payload: {},
}),
);
Listening to events#
To declare an event listener, decorate a method with the @OnEvent() decorator preceding the method definition containing the code to be executed, as follows:
@OnEvent('order.created')
handleOrderCreatedEvent(payload: OrderCreatedEvent) {
// handle and process "OrderCreatedEvent" event
}
Warning Event subscribers cannot be request-scoped.
The first argument can be a string or symbol for a simple event emitter and a string | symbol | Array<string | symbol> in a case of a wildcard emitter.
The second argument (optional) is a listener options object as follows:
export type OnEventOptions = OnOptions & {
/**
* If "true", prepends (instead of append) the given listener to the array of listeners.
*
* @see https://github.com/EventEmitter2/EventEmitter2#emitterprependlistenerevent-listener-options
*
* @default false
*/
prependListener?: boolean;
/**
* If "true", the onEvent callback will not throw an error while handling the event. Otherwise, if "false" it will throw an error.
*
* @default true
*/
suppressErrors?: boolean;
};
Hint Read more about theOnOptionsoptions object fromeventemitter2.
@OnEvent('order.created', { async: true })
handleOrderCreatedEvent(payload: OrderCreatedEvent) {
// handle and process "OrderCreatedEvent" event
}
To use namespaces/wildcards, pass the wildcard option into the EventEmitterModule#forRoot() method. When namespaces/wildcards are enabled, events can either be strings (foo.bar) separated by a delimiter or arrays (['foo', 'bar']). The delimiter is also configurable as a configuration property (delimiter). With namespaces feature enabled, you can subscribe to events using a wildcard:
@OnEvent('order.*')
handleOrderEvents(payload: OrderCreatedEvent | OrderRemovedEvent | OrderUpdatedEvent) {
// handle and process an event
}
Note that such a wildcard only applies to one block. The argument order.* will match, for example, the events order.created and order.shipped but not order.delayed.out_of_stock. In order to listen to such events,
use the multilevel wildcard pattern (i.e, **), described in the EventEmitter2documentation.
With this pattern, you can, for example, create an event listener that catches all events.
@OnEvent('**')
handleEverything(payload: any) {
// handle and process an event
}
HintEventEmitter2class provides several useful methods for interacting with events, likewaitForandonAny. You can read more about them here.
Preventing event loss#
Events triggered before or during the onApplicationBootstrap lifecycle hook—such as those from module constructors or the onModuleInit method—may be missed because the EventSubscribersLoader might not have finished setting up the listeners.
To avoid this issue, you can use the waitUntilReady method of the EventEmitterReadinessWatcher, which returns a promise that resolves once all listeners have been registered. This method can be called in the onApplicationBootstrap lifecycle hook of a module to ensure that all events are properly captured.
await this.eventEmitterReadinessWatcher.waitUntilReady();
this.eventEmitter.emit(
'order.created',
new OrderCreatedEvent({ orderId: 1, payload: {} }),
);
Note This is only necessary for events emitted before the onApplicationBootstrap lifecycle hook is complete.
Example#
A working example is available here.
