@application-kit/bootstrap
Бутстрап процессов NestJS: web на Fastify с периметром, consumer с сервером метрик и CLI на nestjs-console. Состав модулей, контроллеры документа Swagger и хуки метрик знает приложение: пакет собирает процесс из его корневого модуля и опций. Порт, число прокси и порт метрик — переменные окружения контракта чарта; невалидное значение роняет старт.
Установка
yarn add @application-kit/bootstrap
Peer-зависимости — @nestjs/common, @nestjs/core, nestjs-pino; остальные optional и нужны только своему входу.
@application-kit/observability пакет приносит сам. Логгер обоих входов — nestjs-pino: его LoggerModule обязан быть
в графе корневого модуля.
Входы
| Вход | Для чего | Тянет |
|---|---|---|
@application-kit/bootstrap/web | webBootstrap, createFastifyApplication — web-процесс на Fastify | @nestjs/platform-fastify, @fastify/cookie, class-validator, class-transformer, @opentelemetry/api, @libs-for-dev/nestjs-config-module |
@application-kit/bootstrap/consumer | consumerBootstrap — процесс-консьюмер без HTTP-слоя | class-validator, class-transformer, @libs-for-dev/nestjs-config-module |
@application-kit/bootstrap/cli | cliBootstrap — CLI-процесс | nestjs-console |
@application-kit/bootstrap/swagger | setupSwagger — документ OpenAPI web-процесса по выключателю | @nestjs/swagger, class-validator, class-transformer, @libs-for-dev/nestjs-config-module |
Корневого входа нет: web-процессу nestjs-console не нужен, CLI — Fastify.
Окружение
| Переменная | Процесс | По умолчанию | |
|---|---|---|---|
PORT | web | 3000 | порт listen на 0.0.0.0 |
TRUST_PROXY_HOPS | web | 0 | сколько прокси перед приложением доверенные; 0 — только сокет |
METRICS_PORT | consumer | 3001 | порт отдельного сервера /metrics |
SWAGGER_ENABLED | web | false | публиковать ли документ setupSwagger; выключают false, 0 и пустая строка |
Значение числовых переменных — целое не меньше 0 (порт — не больше 65535). Иное (abc, 1.5, -1) — ClassValidationError с именем
переменной до создания приложения: Number('abc') дал бы NaN, и доверенных прокси молча стало бы ноль.
Web
import { webBootstrap } from '@application-kit/bootstrap/web'
void webBootstrap(AppModule, {
configure: (app) => {
setupMetricsHooks(app)
},
})
webBootstrap берёт порт и прокси из окружения, собирает приложение через createFastifyApplication, слушает и
возвращает его. createFastifyApplication — для своего запуска, когда порт и прокси приходят не из окружения:
import { createFastifyApplication } from '@application-kit/bootstrap/web'
const app = await createFastifyApplication(AppModule, {
configure: (app) => {
setupMetricsHooks(app)
},
trustProxyHops: 2,
})
await app.listen(port, '0.0.0.0')
Периметр, который ставит пакет, по порядку:
- Fastify-адаптер:
genReqId—x-request-idот прокси либо свой UUID (fastifyRequestIdиз@application-kit/observability/logging); доверенные прокси — первыеtrustProxyHopsпрыжков справа вX-Forwarded-For, так что адрес, подсунутый клиентом слева,request.ipне подменяет. @fastify/cookieс опциямиcookie.- Хук
onRequest: заголовокx-request-idна каждый ответ, включая 404 и ошибки парсинга тела. configure(app)— настройка продукта.LoggerErrorInterceptorи логгерnestjs-pinoдля Nest;uncaughtException— в тот же лог.- Shutdown hooks: по SIGTERM/SIGINT провайдеры закрываются, HTTP-сервер дожидается открытых соединений.
- Глобальный
ValidationPipeсtransform: true: невалидный DTO — статусvalidationErrorStatusи тело{ statusCode, validationErrors: [{ property, constraints }] }, вложенное поле — путёмparent.child.
| Опция | По умолчанию | |
|---|---|---|
trustProxyHops | 0 | сколько прокси перед приложением доверенные; 0 — только сокет |
validationErrorStatus | 422 | статус ответа на невалидный DTO |
cookie | {} | опции @fastify/cookie; false — плагин не регистрируется |
configure | — | (app) => void, зовётся после хука request-id, до интерцептора и пайпа |
Swagger
import { setupSwagger } from '@application-kit/bootstrap/swagger'
const options = {
configure: (app) => {
setupSwagger(app, { include: [HealthHttpModule, MetricsHttpModule] })
},
} satisfies FastifyApplicationOptionsInterface
Документ описывает всю серверную поверхность и аутентификации не требует, поэтому без SWAGGER_ENABLED он не
публикуется. В документ попадают контроллеры модулей из include, operationId — имя метода. UI отдаётся по path
(по умолчанию swagger), JSON — по <path>-json, версия в info — version (по умолчанию 1.0).
Consumer
import { consumerBootstrap } from '@application-kit/bootstrap/consumer'
import { kafkaConsumerServer } from '@application-kit/kafka'
void consumerBootstrap(ConsumerModule, kafkaConsumerServer)
Сервер /metrics поднимается на METRICS_PORT со своим реестром метрик процесса, затем корневой модуль уходит
транспорту вместе с логгером nestjs-pino. Транспорт передаёт приложение: от брокера пакет не зависит.
CLI
import { cliBootstrap } from '@application-kit/bootstrap/cli'
void cliBootstrap(CliModule)
Команда берётся из process.argv, после неё приложение закрывается. Упавшая команда — запись уровня error с
context: 'Console process failed.' и код выхода 1: cron и джоба кластера видят провал только по коду. Корневой модуль
импортирует ConsoleModule из nestjs-console.