Приложение Nest, как и каждый элемент приложения, имеет жизненный цикл, управляемый Nest. Nest предоставляет хуки жизненного цикла, которые обеспечивают видимость ключевых событий жизненного цикла и возможность действовать (запускать зарегистрированный код в вашем модуле, инжектере или контроллере), когда они происходят.

Последовательность жизненного цикла

На следующей диаграмме показана последовательность ключевых событий жизненного цикла приложения, начиная с момента загрузки приложения и заканчивая завершением процесса. Мы можем разделить общий жизненный цикл на три фазы: инициализация, запуск и завершение. Используя этот жизненный цикл, вы можете планировать соответствующую инициализацию модулей и служб, управлять активными соединениями и изящно завершать работу приложения, когда оно получает сигнал о завершении.

События жизненного цикла

События жизненного цикла происходят во время загрузки и завершения приложения. Nest вызывает зарегистрированные методы lifecycle hook на модулях, инжектах и контроллерах при каждом из следующих событий жизненного цикла (shutdown hooks должны быть сначала включены, как описано ниже). Как показано на диаграмме выше, Nest также вызывает соответствующие базовые методы для начала прослушивания соединений и для прекращения прослушивания соединений.

В следующей таблице onModuleDestroy, beforeApplicationShutdown и onApplicationShutdown срабатывают только если вы явно вызовете app.close() или если процесс получит специальный системный сигнал (например, SIGTERM) и вы правильно вызвали enableShutdownHooks при загрузке приложения (см. ниже Часть "Выключение приложения").

Хуки жизненного циклаСобытие жизненного цикла, инициирующее вызов хука
onModuleInit()Вызывается после разрешения зависимостей главного модуля.
onApplicationBootstrap()Вызывается после инициализации всех модулей, но до прослушивания соединений.
onModuleDestroy()*Вызывается после получения сигнала завершения (например, SIGTERM).
beforeApplicationShutdown()*Вызывается после завершения всех обработчиков onModuleDestroy() (Promises resolved or rejected);
после завершения (Promises resolved or rejected) все существующие соединения будут закрыты (вызывается app.close()).
onApplicationShutdown()*Вызывается после закрытия соединений (резолвится промис app.close()).

* Для этих событий, если вы не вызываете app.close() явно, вы должны сделать выбор, чтобы они работали с системными сигналами, такими как SIGTERM

Перечисленные выше хуки жизненного цикла не срабатывают для классов привязанных к запросам. Классы, привязанные к запросу, не привязаны к жизненному циклу приложения, и их продолжительность жизни непредсказуема. Они создаются исключительно для каждого запроса и автоматически очищаются от мусора после отправки ответа.

Использование

Каждый хук жизненного цикла представлен интерфейсом. Интерфейсы технически необязательны, поскольку они не существуют после компиляции TypeScript. Тем не менее, их использование является хорошей практикой, чтобы воспользоваться преимуществами сильной типизации и инструментария редактора. Чтобы зарегистрировать хук жизненного цикла, реализуйте соответствующий интерфейс. Например, чтобы зарегистрировать метод, который будет вызываться во время инициализации модуля на определенном классе (например, Controller, Provider или Module), реализуйте интерфейс OnModuleInit, предоставив метод onModuleInit(), как показано ниже:

import { Injectable, OnModuleInit } from '@nestjs/common';
@Injectable()
export class UsersService implements OnModuleInit {
  onModuleInit() {
    console.log(`The module has been initialized.`);
  }
}

Асинхронная инициализация

Хуки OnModuleInit и OnApplicationBootstrap позволяют отложить процесс инициализации приложения (возвращая Promise или помечая метод как async и await асинхронного завершения метода в теле метода).

async onModuleInit(): Promise<void> {
  await this.fetch();
}

Завершение работы приложения

Хуки onModuleDestroy(), beforeApplicationShutdown() и onApplicationShutdown() вызываются на этапе завершения работы приложения (в ответ на явный вызов app.close() или при получении системных сигналов, таких как SIGTERM, если это предусмотрено). Эта возможность часто используется в Kubernetes для управления жизненным циклом контейнеров, в Heroku для dynos или подобных сервисов.

Слушатели хуков отключения потребляют системные ресурсы, поэтому по умолчанию они отключены. Чтобы использовать хуки отключения, вы должны включить слушателей, вызвав enableShutdownHooks():

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // Начинаем прослушивать хуки отключения
  app.enableShutdownHooks();
  await app.listen(3000);
}
bootstrap();

Из-за присущих платформе ограничений, NestJS имеет ограниченную поддержку хуков выключения приложения в Windows. Вы можете ожидать, что SIGINT будет работать, а также SIGBREAK и в некоторой степени SIGHUP. Однако SIGTERM никогда не будет работать в Windows, потому что убийство процесса в диспетчере задач является безусловным, "т.е. нет способа для приложения обнаружить или предотвратить его".

enableShutdownHooks потребляет память, запуская слушателей. В случаях, когда вы запускаете несколько приложений Nest в одном процессе Node (например, при выполнении параллельных тестов с Jest), Node может пожаловаться на чрезмерное количество процессов прослушивателей. По этой причине enableShutdownHooks не включен по умолчанию. Помните об этом, если вы запускаете несколько экземпляров в одном процессе Node.

Когда приложение получает сигнал о завершении работы, оно будет вызывать любые зарегистрированные методы onModuleDestroy(), beforeApplicationShutdown(), затем onApplicationShutdown() (в последовательности, описанной выше) с соответствующим сигналом в качестве первого параметра. Если зарегистрированная функция ожидает асинхронного вызова (возвращает обещание), Nest не будет продолжать последовательность, пока промис не будет разрешен или отклонен.

@Injectable()
class UsersService implements OnApplicationShutdown {
  onApplicationShutdown(signal: string) {
    console.log(signal); // например "SIGINT"
  }
}

Вызов app.close() не завершает процесс Node, а только запускает хуки onModuleDestroy() и onApplicationShutdown(), поэтому если есть какие-то интервалы, долго выполняющиеся фоновые задачи и т.д., процесс не будет автоматически завершен.