Что такое dependency injection в NestJS?
Dependency Injection в NestJS
Dependency Injection (внедрение зависимостей) — это паттерн проектирования и одновременно принцип из SOLID (буква «D» — Dependency Inversion Principle). Суть в том, что класс не создаёт свои зависимости самостоятельно через new, а получает их из внешнего источника.
NestJS построен вокруг встроенного IoC-контейнера (Inversion of Control). Разработчику не нужно вручную передавать зависимости — фреймворк делает это автоматически.
Как это работает
Когда вы помечаете класс декоратором @Injectable(), NestJS регистрирует его как провайдер. Контейнер читает типы аргументов конструктора через TypeScript Reflect Metadata и сам разрешает и подставляет нужные экземпляры.
// Сервис помечается как провайдер
@Injectable()
export class CatsService {
private readonly cats: string[] = [];
findAll(): string[] {
return this.cats;
}
}
// Контроллер получает сервис через конструктор
@Controller('cats')
export class CatsController {
// NestJS сам создаст и передаст экземпляр CatsService
constructor(private readonly catsService: CatsService) {}
@Get()
findAll() {
return this.catsService.findAll();
}
}
Регистрация провайдеров в модуле
Провайдер нужно зарегистрировать в модуле в массиве providers. Контроллер, использующий его, — в controllers.
@Module({
controllers: [CatsController],
providers: [CatsService], // регистрируем сервис в контейнере
})
export class CatsModule {}
Область видимости (Scope)
По умолчанию провайдеры — синглтоны: один экземпляр на всё приложение. Можно изменить скоуп:
@Injectable({ scope: Scope.REQUEST })
export class RequestScopedService {
// новый экземпляр создаётся на каждый HTTP-запрос
}
Кастомные провайдеры
DI в NestJS гибко настраивается. Можно подставить mock-объект, фабрику или значение вместо класса:
@Module({
providers: [
{
provide: CatsService,
useClass: MockCatsService, // подменяем реализацию
},
],
})
export class TestModule {}
Зачем это нужно
- Слабая связанность: компоненты не зависят от конкретных реализаций, только от интерфейсов
- Тестируемость: в тестах легко подменить зависимость на mock
- Переиспользование: один провайдер-синглтон доступен во всём модуле без повторного создания
- Управляемость: NestJS берёт на себя управление жизненным циклом объектов
Что хочет услышать интервьюер
Понимание того, что DI — это паттерн, а не функция фреймворка, и что NestJS реализует его через IoC-контейнер
Знание роли декоратора @Injectable() и того, как NestJS использует Reflect Metadata для разрешения зависимостей
Понимание, зачем нужно регистрировать провайдеры в массиве providers модуля
Осознание преимуществ DI: тестируемость, слабая связанность, повторное использование
Базовое понимание, что провайдеры по умолчанию являются синглтонами
Пример: Провайдер с @Injectable()
import { Injectable } from '@nestjs/common';
// Регистрируем класс как провайдер в IoC-контейнере
@Injectable()
export class CatsService {
private readonly cats: string[] = ['Барсик', 'Мурзик'];
findAll(): string[] {
return this.cats;
}
}
Пример: Внедрение через конструктор
import { Controller, Get } from '@nestjs/common';
import { CatsService } from './cats.service';
@Controller('cats')
export class CatsController {
// NestJS сам создаёт экземпляр CatsService и передаёт сюда
constructor(private readonly catsService: CatsService) {}
@Get()
findAll(): string[] {
return this.catsService.findAll();
}
}
Пример: Регистрация провайдера в модуле
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService], // без этого контейнер не знает о провайдере
})
export class CatsModule {}
Типичные ошибки
Путают DI с декоратором @Injectable() — думают, что декоратор сам по себе и есть DI, а не регистрация в контейнере
Забывают добавить провайдер в массив providers модуля и получают ошибку 'No provider for X'
Не понимают разницу между scope: DEFAULT (синглтон) и scope: REQUEST — создают REQUEST-скоупный сервис там, где это не нужно
Не могут объяснить практическое преимущество DI — особенно в контексте тестирования и подмены зависимостей
Думают, что DI в NestJS — это уникальная функция фреймворка, не зная об общей концепции Inversion of Control


