Что такое dependency injection в NestJS?

JuniorNestJS · Backend·Обновлено 26 июня 2026
Коротко
Dependency Injection (DI) — это паттерн, при котором зависимости класса передаются извне, а не создаются внутри него. В NestJS DI реализован через встроенный IoC-контейнер, который автоматически создаёт и подставляет зависимости на основе декораторов.

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

Лучшие курсы по теме

изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Node.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее