Lesson 11-NestJS Lifecycle Events

In NestJS, lifecycle events allow you to execute specific code at particular moments, such as during module initialization or destruction. This helps you manage resources effectively, such as database connections, scheduled tasks, logging, and more.

Below are the primary lifecycle events in NestJS and their usage:

OnModuleInit and OnModuleDestroy

The OnModuleInit interface allows you to perform operations immediately after a module is initialized, while OnModuleDestroy enables cleanup tasks before a module is destroyed.

// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MyModuleInitializer } from './my-module-initializer';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService, MyModuleInitializer],
})
export class AppModule {}

// my-module-initializer.ts
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';

@Injectable()
export class MyModuleInitializer implements OnModuleInit, OnModuleDestroy {
  onModuleInit() {
    console.log('Module initialized!');
  }

  onModuleDestroy() {
    console.log('Module destroyed!');
  }
}

OnInit and OnDestroy

OnInit and OnDestroy are interfaces that can be implemented in components, services, controllers, and other classes to define initialization and destruction operations.

// app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController implements OnInit, OnDestroy {
  constructor(private readonly appService: AppService) {}

  ngOnInit() {
    console.log('Controller initialized!');
  }

  ngOnDestroy() {
    console.log('Controller destroyed!');
  }

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

OnApplicationBootstrap

The OnApplicationBootstrap interface allows you to perform operations after the entire application has started.

// app.service.ts
import { Injectable, OnApplicationBootstrap } from '@nestjs/common';

@Injectable()
export class AppService implements OnApplicationBootstrap {
  constructor() {}

  onApplicationBootstrap() {
    console.log('Application bootstrapped!');
  }

  getHello(): string {
    return 'Hello World!';
  }
}

OnApplicationShutdown

The OnApplicationShutdown interface allows you to perform cleanup tasks before the application shuts down.

// app.service.ts
import { Injectable, OnApplicationShutdown } from '@nestjs/common';

@Injectable()
export class AppService implements OnApplicationShutdown {
  constructor() {}

  onApplicationShutdown(signal?: string) {
    console.log(`Application shutting down! Signal: ${signal}`);
  }

  getHello(): string {
    return 'Hello World!';
  }
}

OnApplicationExit

The OnApplicationExit interface allows you to perform cleanup tasks before the application exits.

// app.service.ts
import { Injectable, OnApplicationExit } from '@nestjs/common';

@Injectable()
export class AppService implements OnApplicationExit {
  constructor() {}

  onApplicationExit(signal?: string) {
    console.log(`Application exiting! Signal: ${signal}`);
  }

  getHello(): string {
    return 'Hello World!';
  }
}

OnHealthCheck

The OnHealthCheck interface allows you to perform operations during health checks.

// health.controller.ts
import { Controller, Get } from '@nestjs/common';
import { HealthCheck, HealthCheckService, OnHealthCheck } from '@nestjs/terminus';

@Controller('health')
export class HealthController implements OnHealthCheck {
  constructor(private health: HealthCheckService) {}

  @HealthCheck()
  check() {
    return this.health.check([]);
  }

  onHealthCheck() {
    console.log('Health check performed!');
  }
}

OnEvent

The OnEvent decorator allows you to execute operations when specific events occur.

// event.listener.ts
import { Injectable, OnEvent } from '@nestjs/common';

@Injectable()
export class EventListener {
  @OnEvent('user.created')
  handleUserCreated(event: any) {
    console.log('User created:', event);
  }
}

OnMicroserviceInit and OnMicroserviceDestroy

These interfaces allow you to perform operations when a microservice starts or is destroyed.

// microservice.service.ts
import { Injectable, OnMicroserviceInit, OnMicroserviceDestroy } from '@nestjs/common';

@Injectable()
export class MicroserviceService implements OnMicroserviceInit, OnMicroserviceDestroy {
  onMicroserviceInit() {
    console.log('Microservice initialized!');
  }

  onMicroserviceDestroy() {
    console.log('Microservice destroyed!');
  }
}

OnGatewayInit, AfterInit, OnGatewayConnection, OnGatewayDisconnect, OnGatewayMessage

These interfaces allow you to perform operations during WebSocket gateway initialization, connection, disconnection, and message receipt.

// chat.gateway.ts
import { WebSocketGateway, OnGatewayInit, AfterInit, OnGatewayConnection, OnGatewayDisconnect, OnGatewayMessage } from '@nestjs/websockets';

@WebSocketGateway()
export class ChatGateway implements OnGatewayInit, AfterInit, OnGatewayConnection, OnGatewayDisconnect, OnGatewayMessage {
  afterInit(server: any) {
    console.log('Server initialized!');
  }

  handleInit() {
    console.log('Gateway initialized!');
  }

  handleConnection(client: any, ...args: any[]) {
    console.log('Client connected:', client.id);
  }

  handleDisconnect(client: any) {
    console.log('Client disconnected:', client.id);
  }

  handleMessage(client: any, payload: any) {
    console.log('Message received:', payload);
  }
}

OnSchedule

The OnSchedule decorator allows you to execute operations when scheduled tasks are triggered.

// scheduler.service.ts
import { Injectable, OnSchedule } from '@nestjs/schedule';

@Injectable()
export class SchedulerService implements OnSchedule {
  @OnSchedule('cron', '*/5 * * * * *')
  executeEveryFiveSeconds() {
    console.log('Task executed every five seconds.');
  }
}

Use Cases for OnModuleInit and OnModuleDestroy

OnModuleInit and OnModuleDestroy are primarily used for module-level initialization and cleanup tasks, such as establishing database connections or starting scheduled tasks.

// database.service.ts
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { createConnection } from 'typeorm';

@Injectable()
export class DatabaseService implements OnModuleInit, OnModuleDestroy {
  async onModuleInit() {
    await createConnection();
    console.log('Database connection established!');
  }

  onModuleDestroy() {
    console.log('Closing database connection...');
  }
}

Comprehensive Example

Below is a comprehensive example demonstrating how to use the above lifecycle events in a simple NestJS application.

// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { DatabaseService } from './database.service';
import { EventListener } from './event.listener';
import { SchedulerService } from './scheduler.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService, DatabaseService, EventListener, SchedulerService],
})
export class AppModule {}

// app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController implements OnInit, OnDestroy {
  constructor(private readonly appService: AppService) {}

  ngOnInit() {
    console.log('Controller initialized!');
  }

  ngOnDestroy() {
    console.log('Controller destroyed!');
  }

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

// app.service.ts
import { Injectable, OnApplicationBootstrap, OnApplicationShutdown, OnApplicationExit } from '@nestjs/common';

@Injectable()
export class AppService implements OnApplicationBootstrap, OnApplicationShutdown, OnApplicationExit {
  constructor() {}

  onApplicationBootstrap() {
    console.log('Application bootstrapped!');
  }

  onApplicationShutdown(signal?: string) {
    console.log(`Application shutting down! Signal: ${signal}`);
  }

  onApplicationExit(signal?: string) {
    console.log(`Application exiting! Signal: ${signal}`);
  }

  getHello(): string {
    return 'Hello World!';
  }
}

// database.service.ts
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { createConnection } from 'typeorm';

@Injectable()
export class DatabaseService implements OnModuleInit, OnModuleDestroy {
  async onModuleInit() {
    await createConnection();
    console.log('Database connection established!');
  }

  onModuleDestroy() {
    console.log('Closing database connection...');
  }
}

// event.listener.ts
import { Injectable, OnEvent } from '@nestjs/common';

@Injectable()
export class EventListener {
  @OnEvent('user.created')
  handleUserCreated(event: any) {
    console.log('User created:', event);
  }

  @OnEvent('user.updated')
  handleUserUpdated(event: any) {
    console.log('User updated:', event);
  }
}

Using OnSchedule for Scheduled Tasks

The following demonstrates how to use OnSchedule to create a scheduled task that runs every minute.

// scheduler.service.ts
import { Injectable, OnSchedule } from '@nestjs/schedule';

@Injectable()
export class SchedulerService implements OnSchedule {
  @OnSchedule('interval', 60000)
  executeEveryMinute() {
    console.log('Task executed every minute.');
  }

  @OnSchedule('cron', '0 * * * *')
  executeHourly() {
    console.log('Task executed hourly.');
  }
}

Using OnHealthCheck for Health Checks

To demonstrate the OnHealthCheck interface, we can create a simple health check controller.

// health.controller.ts
import { Controller, Get } from '@nestjs/common';
import { HealthCheck, HealthCheckService, OnHealthCheck } from '@nestjs/terminus';

@Controller('health')
export class HealthController implements OnHealthCheck {
  constructor(private health: HealthCheckService) {}

  @HealthCheck()
  check() {
    return this.health.check([]);
  }

  onHealthCheck() {
    console.log('Health check performed!');
  }
}

Using OnGatewayInit, AfterInit, OnGatewayConnection, OnGatewayDisconnect, OnGatewayMessage

Below is a simple WebSocket gateway example demonstrating these interfaces.

// chat.gateway.ts
import { WebSocketGateway, OnGatewayInit, AfterInit, OnGatewayConnection, OnGatewayDisconnect, OnGatewayMessage } from '@nestjs/websockets';

@WebSocketGateway()
export class ChatGateway implements OnGatewayInit, AfterInit, OnGatewayConnection, OnGatewayDisconnect, OnGatewayMessage {
  afterInit(server: any) {
    console.log('Server initialized!');
  }

  handleInit() {
    console.log('Gateway initialized!');
  }

  handleConnection(client: any, ...args: any[]) {
    console.log('Client connected:', client.id);
  }

  handleDisconnect(client: any) {
    console.log('Client disconnected:', client.id);
  }

  handleMessage(client: any, payload: any) {
    console.log('Message received:', payload);
  }
}

Using OnMicroserviceInit and OnMicroserviceDestroy

Below is a simple microservice example demonstrating these interfaces.

// microservice.service.ts
import { Injectable, OnMicroserviceInit, OnMicroserviceDestroy } from '@nestjs/common';

@Injectable()
export class MicroserviceService implements OnMicroserviceInit, OnMicroserviceDestroy {
  onMicroserviceInit() {
    console.log('Microservice initialized!');
  }

  onMicroserviceDestroy() {
    console.log('Microservice destroyed!');
  }
}

Using OnModuleInit and OnModuleDestroy

Below is a simple database service example demonstrating these interfaces.

// database.service.ts
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { createConnection } from 'typeorm';

@Injectable()
export class DatabaseService implements OnModuleInit, OnModuleDestroy {
  async onModuleInit() {
    await createConnection();
    console.log('Database connection established!');
  }

  onModuleDestroy() {
    console.log('Closing database connection...');
  }
}

Complete AppModule

Finally, we can register all services and controllers in the AppModule.

// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { DatabaseService } from './database.service';
import { EventListener } from './event.listener';
import { SchedulerService } from './scheduler.service';
import { ChatGateway } from './chat.gateway';
import { MicroserviceService } from './microservice.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService, DatabaseService, EventListener, SchedulerService, ChatGateway, MicroserviceService],
})
export class AppModule {}

Running the Application

You can now run the application and observe the console output to verify that the lifecycle events are triggered as expected.

# Install dependencies
npm install

# Start the application
npm run start:dev

Testing

  • Visit the /health endpoint to trigger a health check.
  • Use a WebSocket client to connect to the /ws/chat endpoint to trigger connection and message events.
  • Trigger scheduled tasks and observe the console output.

Summary

  • Initialization Events: Such as OnModuleInit, OnInit, OnApplicationBootstrap, OnGatewayInit, and OnMicroserviceInit, are used to execute initialization logic at startup.
  • Destruction Events: Such as OnModuleDestroy, OnDestroy, OnApplicationShutdown, OnGatewayDestroy, and OnMicroserviceDestroy, are used to perform cleanup logic during shutdown.
  • Other Events: Such as OnEvent, OnHealthCheck, and OnSchedule, are used to respond to specific events or periodic tasks.

Share your love