NestJSMiddleTechnical
Как работают lifecycle hooks (onModuleInit, onApplicationShutdown) и где они нужны?
Lifecycle hooks NestJS: onModuleInit выполняется после DI-инициализации модуля (подключение к БД, прогрев кэша), onApplicationShutdown — при завершении процесса (закрытие соединений, flush буферов).
Lifecycle Hooks в NestJS
Полный жизненный цикл приложения
onModuleInit— после инициализации зависимостей модуля.onApplicationBootstrap— после инициализации всех модулей.onModuleDestroy— перед уничтожением модуля.beforeApplicationShutdown— получает сигнал OS (SIGTERM и т.д.).onApplicationShutdown— финальная очистка.
onModuleInit — инициализация при старте
import { Injectable, OnModuleInit } from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
@Injectable()
export class DatabaseService implements OnModuleInit {
constructor(@InjectDataSource() private dataSource: DataSource) {}
async onModuleInit() {
// Проверить соединение с БД при старте
await this.dataSource.query('SELECT 1');
console.log('Database connection verified');
}
}
Прогрев кэша
@Injectable()
export class ConfigCacheService implements OnModuleInit {
private settings: Map<string, string> = new Map();
constructor(private readonly settingsRepo: SettingsRepository) {}
async onModuleInit() {
const all = await this.settingsRepo.findAll();
for (const s of all) {
this.settings.set(s.key, s.value);
}
console.log(`Loaded ${this.settings.size} settings into cache`);
}
get(key: string): string | undefined {
return this.settings.get(key);
}
}
onApplicationShutdown — graceful shutdown
// main.ts — обязательно включить!
const app = await NestFactory.create(AppModule);
app.enableShutdownHooks();
// Перехват сигналов OS
process.on('SIGTERM', () => app.close());
process.on('SIGINT', () => app.close());
// queue.service.ts
@Injectable()
export class QueueService implements OnApplicationShutdown {
private consumer: Consumer;
async onApplicationShutdown(signal?: string) {
console.log(`Received shutdown signal: ${signal}`);
// Дождаться обработки текущих сообщений
await this.consumer.disconnect();
await this.producer.disconnect();
}
}
// http-server.service.ts
@Injectable()
export class ServerService implements BeforeApplicationShutdown {
beforeApplicationShutdown(signal?: string) {
console.log(`Preparing for shutdown, signal: ${signal}`);
// Перестать принимать новые соединения
}
}
onApplicationBootstrap — пост-инициализация
@Injectable()
export class SchedulerService implements OnApplicationBootstrap {
constructor(private schedulerRegistry: SchedulerRegistry) {}
onApplicationBootstrap() {
// Безопасно запускать cron после инициализации всех модулей
const job = new CronJob('0 */6 * * *', () => this.runSync());
this.schedulerRegistry.addCronJob('data-sync', job);
job.start();
}
}
Подводные камни
app.enableShutdownHooks()обязателен — без негоOnApplicationShutdownне вызывается при SIGTERM, и процесс завершается мгновенно без очистки.- В Lambda / serverless окружениях
onApplicationShutdownне вызывается при cold start timeouts — используйте try/finally в обработчиках. onModuleInitвыполняется синхронно в порядке инициализации модулей; долгая операция блокирует запуск приложения — добавьте timeout или retry.- Если
onModuleInitбросает исключение, приложение не запустится; оберните нестабильные операции в try/catch с корректным логированием. - В тестах (Test.createTestingModule) lifecycle hooks вызываются только после явного вызова
moduleRef.init()— без негоonModuleInitне выполнится. - Порядок вызова shutdown hooks недетерминирован между разными сервисами; не полагайтесь на то, что один сервис завершится раньше другого.
onModuleDestroyvsonApplicationShutdown: первый вызывается при уничтожении конкретного модуля, второй — всего приложения; для финального освобождения ресурсов используйте shutdown.
Common mistakes
- Отвечать определением без production-сценария.
- Не называть runtime boundary, security boundary или failure mode.
- Игнорировать версию API, observability и тестовую проверку.
What the interviewer is testing
- Объясняет механизм своими словами и без выдуманных API.
- Называет реальные риски, диагностику и критерий корректности.
- Связывает ответ с текущей документацией и миграционными ограничениями.