Event-driven architecture is a powerful design pattern that promotes loose coupling, scalability, and better organization of logic. In NestJS, you can leverage the built-in event emitter system to handle events asynchronously within your application.
In this module, we’ll explore how to use @nestjs/event-emitter
to emit and handle events, process them asynchronously, and integrate this mechanism cleanly into your NestJS apps.
Table of Contents
- What Are Event Emitters in NestJS?
- When to Use Event Emitters
- Installing
@nestjs/event-emitter
- Emitting Events
- Listening to Events with Listeners
- Async Event Handling
- Real-World Use Cases
- Best Practices
- Conclusion
What Are Event Emitters in NestJS?
NestJS offers first-class support for event-driven patterns using the @nestjs/event-emitter
package. This allows different parts of your application to communicate indirectly through events, making your architecture more modular and decoupled.
Think of it as the pub-sub pattern:
- Emitters publish events.
- Listeners subscribe to and respond to events.
When to Use Event Emitters
Use event emitters when:
- You want to decouple concerns (e.g., send a welcome email after registration).
- You want to trigger background tasks asynchronously.
- You need to notify other parts of your app without circular dependencies.
Installing @nestjs/event-emitter
Install the official event emitter module:
bashCopyEditnpm install @nestjs/event-emitter
Then import the module in your root or feature module:
tsCopyEdit// app.module.ts
import { Module } from '@nestjs/common';
import { EventEmitterModule } from '@nestjs/event-emitter';
import { UserModule } from './user/user.module';
@Module({
imports: [
EventEmitterModule.forRoot(), // globally sets up emitter
UserModule,
],
})
export class AppModule {}
Emitting Events
You can inject EventEmitter2
and emit custom events from any service.
tsCopyEdit// user.service.ts
import { Injectable } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
@Injectable()
export class UserService {
constructor(private eventEmitter: EventEmitter2) {}
async registerUser(data: any) {
// logic to create user
const user = { id: 1, email: data.email };
// Emit event after user is registered
this.eventEmitter.emit('user.registered', user);
}
}
You can emit any object, payload, or metadata you want.
Listening to Events with Listeners
Now you need a listener to react to the emitted event.
tsCopyEdit// user.listener.ts
import { OnEvent } from '@nestjs/event-emitter';
import { Injectable } from '@nestjs/common';
@Injectable()
export class UserListener {
@OnEvent('user.registered')
handleUserRegisteredEvent(payload: any) {
console.log(`User registered:`, payload);
// e.g., send welcome email or log activity
}
}
Make sure to register the listener in your module:
tsCopyEdit// user.module.ts
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserListener } from './user.listener';
@Module({
providers: [UserService, UserListener],
})
export class UserModule {}
Async Event Handling
Event handlers can be asynchronous by returning a Promise.
tsCopyEdit@OnEvent('user.registered')
async handleUserRegisteredEvent(payload: any) {
await this.mailService.sendWelcomeEmail(payload.email);
}
You can also define event priorities and set wildcards for broader patterns.
Example with wildcard:
tsCopyEdit@OnEvent('user.*')
handleAllUserEvents(payload: any) {
console.log('User event occurred:', payload);
}
Real-World Use Cases
- User registration → Send welcome emails.
- Order placement → Send confirmation and trigger invoice generation.
- Password reset → Log the attempt and notify via email.
- Notifications → Trigger real-time socket events based on app activity.
Best Practices
- Use strongly typed event payloads for better maintainability.
- Don’t perform heavy tasks directly in listeners—offload to background jobs if needed.
- Avoid overusing emitters for operations that should be handled via service method calls (keep them purposeful).
- Structure listeners in separate files/modules for clarity.
Conclusion
Event Emitters in NestJS provide a powerful abstraction for decoupling your application logic and implementing reactive flows. By emitting and listening to events, you make your application more modular, maintainable, and ready for scalability.