Что такое lifecycle hooks в NestJS?
Lifecycle hooks в NestJS
NestJS предоставляет набор lifecycle hooks — специальных методов, которые фреймворк вызывает в определённые моменты существования компонента (модуля, провайдера, контроллера). Это позволяет выполнять инициализацию, подключение к ресурсам и корректное завершение работы.
Основные хуки
Все хуки определяются через интерфейсы из пакета @nestjs/common:
OnModuleInit— вызывается после того, как все зависимости модуля разрешены. Используется для инициализации, которая требует наличия внедрённых зависимостей.OnApplicationBootstrap— вызывается после того, как все модули инициализированы и приложение готово к приёму запросов.OnModuleDestroy— вызывается перед тем, как NestJS начинает разрушать модуль. Используется для очистки ресурсов.BeforeApplicationShutdown— вызывается при получении сигнала завершения (SIGTERM, SIGINT и т.д.). Получает сам сигнал в качестве аргумента.OnApplicationShutdown— вызывается после того, как соединения закрыты и приложение завершает работу.
Порядок вызова хуков
При запуске:
OnModuleInit— для каждого модуляOnApplicationBootstrap— для каждого модуля
При завершении:
BeforeApplicationShutdown— для каждого модуляOnModuleDestroy— для каждого модуляOnApplicationShutdown— для каждого модуля
Поддержка async
Все lifecycle-методы могут быть асинхронными. NestJS дожидается завершения промиса перед переходом к следующему шагу.
Shutdown hooks и сигналы ОС
Для корректной обработки завершения приложения необходимо явно включить прослушивание сигналов:
app.enableShutdownHooks();
Без этого BeforeApplicationShutdown и OnApplicationShutdown не будут вызываться при получении системных сигналов.
Когда использовать
OnModuleInit— подключение к базе данных, загрузка конфигурации, инициализация кэшаOnApplicationBootstrap— регистрация в service discovery, старт фоновых задачOnModuleDestroy/OnApplicationShutdown— закрытие соединений с БД, очистка очередей, завершение consumer-ов Kafka/RabbitMQ
Пример реализации
См. примеры кода ниже.
Что хочет услышать интервьюер
Кандидат знает названия основных хуков и понимает, на каком этапе каждый из них вызывается
Понимает разницу между OnModuleInit и OnApplicationBootstrap — особенно что к моменту bootstrap все модули уже инициализированы
Знает, что хуки могут быть асинхронными и NestJS корректно ожидает их завершения
Понимает необходимость вызова enableShutdownHooks() для обработки системных сигналов
Может привести практический пример: подключение к БД в OnModuleInit, закрытие соединений в OnApplicationShutdown
Пример: Реализация lifecycle hooks в провайдере
import {
Injectable,
OnModuleInit,
OnApplicationBootstrap,
OnModuleDestroy,
OnApplicationShutdown,
} from '@nestjs/common';
import { DatabaseService } from './database.service';
@Injectable()
export class AppService
implements
OnModuleInit,
OnApplicationBootstrap,
OnModuleDestroy,
OnApplicationShutdown
{
constructor(private readonly db: DatabaseService) {}
// Вызывается после разрешения всех зависимостей модуля
async onModuleInit(): Promise<void> {
await this.db.connect();
console.log('База данных подключена');
}
// Вызывается после инициализации всех модулей
async onApplicationBootstrap(): Promise<void> {
await this.db.runMigrations();
console.log('Миграции выполнены, приложение готово');
}
// Вызывается при начале разрушения модуля
async onModuleDestroy(): Promise<void> {
console.log('Модуль уничтожается');
}
// Вызывается при завершении работы приложения
async onApplicationShutdown(signal: string): Promise<void> {
console.log(`Получен сигнал: ${signal}`);
await this.db.disconnect();
console.log('Соединение с БД закрыто');
}
}
Пример: Включение shutdown hooks в main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Обязательно для корректной обработки SIGTERM / SIGINT
app.enableShutdownHooks();
await app.listen(3000);
console.log('Приложение запущено на порту 3000');
}
bootstrap();
Пример: Lifecycle hook в модуле
import { Module, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { KafkaService } from './kafka.service';
@Module({
providers: [KafkaService],
exports: [KafkaService],
})
export class MessagingModule implements OnModuleInit, OnModuleDestroy {
constructor(private readonly kafka: KafkaService) {}
async onModuleInit(): Promise<void> {
// Подключаем consumer при инициализации модуля
await this.kafka.connect();
await this.kafka.subscribe('user-events');
}
async onModuleDestroy(): Promise<void> {
// Отключаем consumer при уничтожении модуля
await this.kafka.disconnect();
}
}
Типичные ошибки
Путают OnModuleInit и OnApplicationBootstrap: думают, что это одно и то же, не понимая разницы в порядке вызова
Забывают вызвать app.enableShutdownHooks() и удивляются, почему shutdown-хуки не срабатывают
Не делают lifecycle-методы асинхронными, когда внутри есть асинхронные операции (например, await db.connect()), что приводит к race condition
Пытаются использовать constructor вместо OnModuleInit для инициализации, требующей DI-зависимостей, хотя зависимости уже доступны в constructor
Не закрывают соединения в shutdown-хуках, что приводит к «зависанию» процесса при завершении


