NestJS — серверный фреймворк для Node.js, который строится вокруг модулей, внедрения зависимостей и явных слоёв. Он не навязывает базу данных и шаблонизатор, но задаёт структуру, благодаря которой проект переживает рост команды. Ниже — практическая раскладка каталогов, настройка конфигурации, валидация, работа с БД и запуск в production.

Каркас проекта

CLI создаёт рабочий скелет с TypeScript, тестами и скриптами сборки. Сразу включите строгий режим компилятора: он ловит ошибки типов там, где JavaScript молча отдал бы undefined в ответ API.

npm i -g @nestjs/cli
nest new api --package-manager npm --strict
cd api
npm run start:dev

Раскладка, которая хорошо масштабируется, выглядит так: src/modules — бизнес-модули, src/common — фильтры, интерцепторы и декораторы, src/config — схема переменных окружения, src/database — подключение и миграции. Каждый модуль держит контроллер, сервис, DTO и, при необходимости, репозиторий. Плоский список файлов в корне через полгода превращается в свалку.

Модули и внедрение зависимостей

Модуль — единица композиции. Он объявляет провайдеры и решает, что экспортировать наружу. Сервис не создаётся через new в контроллере: Nest подставляет его по типу конструктора, а контейнер сам разбирает граф зависимостей.

nest g module users
nest g controller users --no-spec
nest g service users --no-spec

Глобальные вещи подключают один раз в AppModule: конфигурацию, логгер, подключение к базе. Если модуль начинает импортировать полпроекта, это сигнал, что границы проведены неверно. Разрывайте циклические зависимости через события или отдельный модуль-посредник, а не через forwardRef по умолчанию.

Конфигурация и валидация

Переменные окружения читаются через @nestjs/config с обязательной схемой. Приложение должно падать на старте, если нет строки подключения или секрета, а не в момент первого запроса. Схему удобно описывать на class-validator: тогда ошибка конфигурации выглядит как обычная ошибка валидации с понятным текстом.

// src/config/env.validation.ts
import { IsIn, IsNumberString, IsString } from 'class-validator';

export class Env {
  @IsIn(['development', 'production', 'test']) NODE_ENV: string;
  @IsNumberString() PORT: string;
  @IsString() DATABASE_URL: string;
  @IsString() JWT_SECRET: string;
}

В main.ts включают глобальную валидацию входящих данных: whitelist отбрасывает лишние поля, forbidNonWhitelisted возвращает ошибку, transform приводит типы. Без этого DTO остаётся декорацией, а в сервис приходят произвольные объекты.

app.useGlobalPipes(new ValidationPipe({
  whitelist: true,
  forbidNonWhitelisted: true,
  transform: true,
}));
app.setGlobalPrefix('api');
app.enableShutdownHooks();

Слой данных

Выбор между TypeORM и Prisma не критичен, критична изоляция. Контроллер не должен знать про SQL, а сервис — про формат HTTP-ответа. Репозиторий возвращает доменные объекты, сервис применяет правила, контроллер только переводит их в DTO.

// src/modules/users/users.service.ts
@Injectable()
export class UsersService {
  constructor(private readonly prisma: PrismaService) {}

  findAll(): Promise<User[]> {
    return this.prisma.user.findMany({ orderBy: { createdAt: 'desc' } });
  }
}

Миграции держите в репозитории и применяйте отдельной командой на деплое, а не автоматически при старте в нескольких репликах. Параллельный запуск миграций — частая причина гонок и полупустых таблиц. Для чтения сложных отчётов используйте отдельные запросы, не пытайтесь собрать всё через жадную загрузку связей.

Ошибки, логи и здоровье

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

nest g filter common/http-exception
nest g interceptor common/logging
nest g guard auth/jwt

Для проверки живости подключают @nestjs/terminus: отдельный эндпоинт проверяет процесс, память и доступность базы. Readiness-проверка не должна ходить во внешние API — иначе кратковременный сбой партнёра выведет из балансировки весь кластер. Ограничитель запросов @nestjs/throttler ставят на публичные маршруты, а не глобально на всё подряд.

Тесты и границы модулей

Юнит-тесты пишут на сервисы, а не на контроллеры: у сервиса есть зависимости, которые подменяются провайдерами Nest. Для проверки HTTP-слоя поднимают приложение через Test.createTestingModule и вызывают эндпоинт целиком — так ловятся ошибки пайпов, гвардов и формата ответа, которые модульные тесты не видят.

npm run test
npm run test:e2e
npm run lint -- --fix

Держите тесты рядом с кодом и обновляйте их вместе с контрактом API. Если тест приходится переписывать при каждом изменении внутренностей, значит, он проверяет реализацию, а не поведение. Для внешних сервисов используйте подставные адаптеры: сеть в тестах должна быть предсказуемой.

Сборка и запуск

В production запускается скомпилированный JavaScript, а не ts-node. Сборка кладёт файлы в dist, переменные окружения передаются снаружи. Процесс должен корректно завершаться по SIGTERM: Nest закроет HTTP-сервер и соединения с базой, не оборвав запросы на середине.

npm ci
npm run build
npm prune --omit=dev
node dist/main.js

Для нескольких ядер запускают несколько экземпляров за reverse proxy либо используют cluster-модуль Node.js с общим портом. Держите в памяти минимум состояния: сессии — в Redis, файлы — в объектном хранилище, кэш — с явным TTL. Тогда горизонтальное масштабирование сводится к добавлению процесса, а не к переписыванию кода.

Отдельно продумайте миграции и откат: сборка должна быть воспроизводимой, версия образа — неизменной, а схема базы совместимой с предыдущим релизом на время выката. Синие-зелёные или канареечные схемы требуют, чтобы старая и новая версии умели работать с одной базой одновременно. Проверьте это на стенде до первого рабочего деплоя и зафиксируйте порядок отката в регламенте команды. Логи пишите в stdout: их соберёт система. Это экономит время при разборе инцидентов.

Чек-лист перед production

  • секреты только в окружении;
  • валидация включена глобально;
  • health-эндпоинт не зависит от внешних API;
  • миграции применяются отдельным шагом.

Итог

NestJS выигрывает не магией, а дисциплиной: модуль, сервис, DTO и явная конфигурация задают предсказуемую структуру. Начните с одного доменного модуля, настройте строгую валидацию окружения и единый формат ошибок, затем добавляйте базу и фоновые задачи. Такой каркас проще тестировать, деплоить и передавать новой команде — а именно это и требуется от серверного Node.js в 2026 году.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте