Основные концепции NestJS

Обзор

NestJS — это прогрессивный Node.js-фреймворк для создания эффективных, масштабируемых и поддерживаемых серверных приложений. Он построен на TypeScript и вдохновлён модульной архитектурой Angular. Фреймворк сочетает элементы объектно-ориентированного (OOP), функционального (FP) и реактивного функционального (FRP) программирования.

NestJS диктует чёткую архитектуру вокруг нескольких ключевых концепций: Modules, Controllers, Providers, Decorators, Dependency Injection, Middleware, Guards, Interceptors, Pipes и Exception Filters. Понимание каждой из них и порядка их выполнения в жизненном цикле запроса — основа грамотной работы с фреймворком.


1. Модули (Modules)

Модули — это базовые строительные блоки NestJS. Каждое приложение имеет как минимум один корневой модуль (AppModule), от которого Nest строит граф зависимостей приложения. Модуль — это класс с декоратором @Module(), который принимает четыре свойства

  • controllers — список контроллеров модуля
  • providers — сервисы, репозитории и другие провайдеры
  • imports — другие модули, от которых зависит текущий
  • exports — провайдеры, доступные другим модулям

Модули поддерживают также глобальный режим (@Global()) и динамическую конфигурацию (например, ConfigModule.forRoot()). Модульная архитектура обеспечивает разделение ответственностей, переиспользование кода и масштабируемость.

@Module({
    controllers: [UsersController],
    providers: [UsersService],
})
export class UsersModule {}

2. Контроллеры (Controllers)

Контроллеры отвечают за обработку входящих HTTP-запросов и отправку ответов клиенту. Механизм маршрутизации определяет, какой контроллер обработает конкретный запрос. Контроллер — это класс с декоратором @Controller(), внутри которого методы помечаются декораторами HTTP-методов (@Get(), @Post(), @Put(), @Delete() и др.).

@Controller('users')
export class UsersController {
    constructor(private usersService: UsersService) {}
 
    @Get()
    getUsers() {
        return this.usersService.findAll();
    }
}

Контроллер принимает зависимости через механизм Dependency Injection (DI), не создавая их вручную.


3. Провайдеры (Providers)

Провайдеры — это классы, которые можно внедрить в другие компоненты через DI. К ним относятся:

  • Сервисы (Services) — содержат бизнес-логику
  • Репозитории (Repositories) — инкапсулируют работу с данными
  • Фабрики (Factories) — создают объекты по условию
  • Хелперы (Helpers) — вспомогательные утилиты

Провайдер помечается декоратором @Injectable(). Время жизни провайдеров по умолчанию совпадает со временем жизни всего приложения (singleton).

@Injectable()
export class UsersService {
    findAll() {
        return ['user1', 'user2', 'user3'];
    }
}

4. Dependency Injection (DI)

Внедрение зависимостей — одна из самых мощных возможностей NestJS. DI позволяет классам автоматически получать свои зависимости без ручного создания их экземпляров. Nest управляет контейнером DI: при запросе на создание класса он автоматически разрешает и инжектирует все необходимые зависимости.

DI в NestJS строится на принципе Inversion of Control (IoC): зависимости передаются извне, что делает код более тестируемым, гибким и слабосвязанным.


5. Декораторы (Decorators)

Декораторы — фундаментальный механизм NestJS для связывания классов и методов с метаданными. NestJS использует TypeScript-декораторы совместно с библиотекой reflect-metadata для хранения и чтения метаданных во время выполнения. Декораторы применяются на уровне классов, методов, свойств и параметров.

Основные встроенные декораторы:

  • @Module() — объявление модуля
  • @Controller() — объявление контроллера
  • @Injectable() — объявление провайдера
  • @Get(), @Post() и др. — маршруты
  • @Body(), @Param(), @Query() — извлечение данных запроса

NestJS также позволяет создавать кастомные декораторы для ролей, логирования, флагов функций и других сквозных задач.


6. Middleware (Посредники)

Middleware — это функции, выполняющиеся до обработчика маршрута. Они имеют доступ к объектам Request и Response, а также к функции next(). Middleware выполняются строго в порядке регистрации по принципу FIFO.

Типичные задачи Middleware:

  • Логирование запросов
  • Аутентификация и авторизация
  • Трансформация данных запроса
  • Обработка CORS, Helmet и других заголовков

Middleware могут быть глобальными (через app.use()) или применяться к конкретным маршрутам и модулям.


7. Guards (Стражи)

Guards определяют, будет ли обработан запрос, основываясь на условиях — например, проверке токена или роли пользователя. Guard возвращает true или false: при false Nest автоматически бросает ForbiddenException. Guards реализуют интерфейс CanActivate.

Guards идеально подходят для:

  • Аутентификации (проверка JWT-токена)
  • Авторизации (проверка ролей, прав доступа)
  • Условного пропуска запроса

8. Interceptors (Перехватчики)

Interceptors вдохновлены паттерном Aspect-Oriented Programming (AOP) и позволяют добавлять логику до и после выполнения обработчика маршрута. Они имеют доступ к потоку выполнения через RxJS-операторы.

Основные применения Interceptors:

  • Логирование запросов и ответов
  • Кэширование (возврат кэшированного ответа)
  • Трансформация ответа (оборачивание в стандартную структуру)
  • Обработка ошибок (перехват и преобразование исключений)

9. Pipes (Конвейеры)

Pipes выполняют валидацию и трансформацию входных данных. Pipe применяется непосредственно перед вызовом обработчика маршрута и работает с аргументами, переданными этому обработчику. Если валидация не прошла — Pipe бросает исключение.

Встроенные pipes NestJS:

  • ValidationPipe — валидация DTO через class-validator
  • ParseIntPipe, ParseUUIDPipe — преобразование типов
  • DefaultValuePipe — подстановка значений по умолчанию

10. Exception Filters (Фильтры исключений)

Exception Filters перехватывают все необработанные исключения в приложении и формируют из них корректный HTTP-ответ. Фильтры можно применять глобально, на уровне контроллера или конкретного маршрута.

Основные применения:

  • Стандартизация формата ошибок
  • Логирование исключений
  • Конвертация не-HTTP исключений в HTTP-ответы

Жизненный цикл запроса

Порядок выполнения компонентов при каждом входящем запросе строго определён:

ЭтапКомпонентНазначение
1MiddlewareПервичная обработка запроса (логирование, CORS)
2GuardsАутентификация и авторизация
3Interceptors (до)Логика перед обработчиком
4PipesВалидация и трансформация данных
5Route HandlerОсновная бизнес-логика контроллера
6Interceptors (после)Логика после обработчика, трансформация ответа
Exception FiltersПерехват ошибок на любом этапе

Понимание этого порядка критично для правильного размещения кода и предотвращения трудноуловимых багов.


Итоговая схема архитектуры

AppModule
├── Controllers   → принимают HTTP-запросы
├── Providers     → бизнес-логика, DI
└── Imports       → другие модули

Запрос → Middleware → Guards → Interceptors → Pipes → Handler → Interceptors → Ответ
                                                              ↕
                                                    Exception Filters

NestJS диктует “opinionated” (строго структурированный) подход к разработке: каждый элемент имеет чёткую роль, а соблюдение этих концепций обеспечивает создание тестируемых, масштабируемых и легко поддерживаемых приложений.

См. также

  • angular — Nest вдохновлён модульной архитектурой Angular
  • solid — DI реализует принцип Dependency Inversion