Sling Academy
Home/Node.js/How to Manage Events in NestJS

How to Manage Events in NestJS

Last updated: January 01, 2024

Introduction

Managing events is crucial for building reactive NestJS applications. This guide walks you through various ways to handle events, enabling loose coupling and enhanced modularity.

Event-Driven Architecture in NestJS

Event-driven architecture within NestJS revolves around the concepts of events and handlers. Events are significant occurrences that your application needs to respond to, and handlers are the logical pieces of code that react to these events.

Let’s install the necessary dependencies for working with events in NestJS:

npm install --save @nestjs/cqrs @nestjs/event-emitter

Creating Basic Events and Handlers

To begin, we’ll create a simple user-creation event and handler.

Define an event:

import { IEvent } from '@nestjs/cqrs';

export class UserCreatedEvent implements IEvent {
  constructor(public readonly userId: string) {}
}

Implement the handler:

import { EventsHandler, IEventHandler } from '@nestjs/cqrs';
import { UserCreatedEvent } from './user-created.event';

@EventsHandler(UserCreatedEvent)
export class UserCreatedEventHandler implements IEventHandler {
  handle(event: UserCreatedEvent) {
    console.log('User created with ID:', event.userId);
  }
}

Setting Up the Event Module

For our events and handlers to work, we need to set up an event module. Here’s how you can do it:

import { Module } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
import { UserCreatedEvent } from './events/user-created.event';
import { UserCreatedEventHandler } from './handlers/user-created.handler';

@Module({
  imports: [CqrsModule],
  providers: [UserCreatedEventHandler]
})
export class EventModule {}

Pub/Sub with NestJS Event Emitter

NestJS provides an EventEmitterModule for publishing and subscribing to events:

import { Module } from '@nestjs/common';
import { EventEmitterModule } from '@nestjs/event-emitter';

@Module({
  imports: [EventEmitterModule.forRoot()],
  ...
})
export class AppModule {}

Define the event:

export class UserCreatedEvent {
  constructor(public readonly userId: string) {}
}

Publish an event:

import { EventEmitter2 } from 'eventemitter2';

constructor(private eventEmitter: EventEmitter2) {}

createUser(userId: string): void {
  this.eventEmitter.emit('user.created', new UserCreatedEvent(userId));
}

Subscribe to the event:

import { OnEvent } from '@nestjs/event-emitter';

@OnEvent('user.created')
handleUserCreatedEvent(event: UserCreatedEvent) {
  console.log('User created with ID:', event.userId);
}

Advanced Usage

Asynchronous Event Handling

In more complex systems, you might need to handle events asynchronously. Let’s delve into implementing a robust event handling system:

Use a queue-based solution for deferred processing:

import { IEvent, ofType, SagaIterator } from '@nestjs/cqrs';
import { delay, map } from 'rxjs/operators';
import { Observable } from 'rxjs';

@saga(processUserCreatedEvent$) {
  return events$
    .pipe(
      ofType(UserCreatedEvent),
      delay(1000),
      map(event => new DeferredEvent(event.userId))
    );
}

Integrating Microservices with Events

In a microservices-based application, NestJS can help ensure reliable communication between services through events:

Example using a message broker:

import { ClientKafka } from '@nestjs/microservices';

constructor(private client: ClientKafka) {}

async onModuleInit() {
  this.client.subscribeToResponseOf('user.created');
  await this.client.connect();
}

publishUserCreatedEvent(userId: string) {
  this.client.emit('user.created', new UserCreatedEvent(userId));
}

Testing Event Handlers

To ensure the reliability of your event-driven system, testing your handlers is essential:

import { Test } from '@nestjs/testing';
import { EventEmitterModule } from '@nestjs/event-emitter';
import { UserCreatedEvent } from './events/user-created.event';
import { UserCreatedEventHandler } from './handlers/user-created.handler';

describe('UserCreatedEventHandler', () => {
  let handler: UserCreatedEventHandler;

  beforeEach(async () => {
    const moduleRef = await Test.createTestingModule({
      imports: [EventEmitterModule.forRoot()],
      providers: [UserCreatedEventHandler],
    }).compile();

    handler = moduleRef.get(UserCreatedEventHandler);
  });

  it('should handle the event', () => {
    const userId = '1';
    const event = new UserCreatedEvent(userId);

    // Assume we mock our side effects here

    handler.handle(event);

    // Assert the expected side effects
  });
});

Conclusion

This guide has walked you through setting up basic to advanced event handling in your NestJS application. Events promote flexible and loosely-coupled system design, allowing your services to react and communicate effectively.

Next Article: 3 Ways to Send Emails in NestJS

Previous Article: How to Compress Response Payload in NestJS

Series: Nest.js Tutorials: From Basics to Advanced

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates