← Назад к вопросам

Есть ли Singleton в NestJS?

1.7 Middle🔥 181 комментариев
#Архитектура и паттерны#Фреймворки и библиотеки

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Singleton в NestJS

Да, Singleton в NestJS существует и используется по умолчанию. Это один из трёх типов Scope (области видимости) инстансов в Nest.js фреймворке.

Три типа Scope в NestJS

  1. DEFAULT (Singleton) — один инстанс на всё приложение
  2. REQUEST — новый инстанс для каждого HTTP запроса
  3. TRANSIENT — новый инстанс при каждом запросе зависимости

Singleton (DEFAULT) - по умолчанию

По умолчанию все провайдеры (services) в NestJS создаются как Singleton. Это означает, что при запуске приложения создаётся один инстанс сервиса, который переиспользуется для всех запросов.

import { Injectable } from '@nestjs/common';

@Injectable()
export class UsersService {
  constructor() {
    console.log('UsersService инстанс создан');
  }

  getUser(id: string) {
    return { id, name: 'John' };
  }
}

В этом случае console.log выведется только один раз при запуске приложения, даже если будет 1000 HTTP запросов.

Явное указание DEFAULT scope

import { Injectable, Scope } from '@nestjs/common';

@Injectable({ scope: Scope.DEFAULT })
export class UsersService {
  // Один инстанс на всё приложение
}

Практический пример: разница между Scope

import { Injectable, Scope } from '@nestjs/common';

// Singleton - один инстанс на приложение
@Injectable({ scope: Scope.DEFAULT })
export class SingletonService {
  private counter = 0;

  increment() {
    return ++this.counter;
  }
}

// Request - новый инстанс на каждый запрос
@Injectable({ scope: Scope.REQUEST })
export class RequestService {
  private counter = 0;

  increment() {
    return ++this.counter;
  }
}

// Контроллер
@Controller()
export class TestController {
  constructor(
    private singletonService: SingletonService,
    private requestService: RequestService,
  ) {}

  @Get('test')
  test() {
    return {
      singleton: this.singletonService.increment(),  // 1, 2, 3, 4...
      request: this.requestService.increment(),      // всегда 1
    };
  }
}

При первом запросе вернёт {singleton: 1, request: 1}, при втором {singleton: 2, request: 1}, при третьем {singleton: 3, request: 1}.

REQUEST Scope

@Injectable({ scope: Scope.REQUEST })
export class RequestContextService {
  private requestId: string;

  setRequestId(id: string) {
    this.requestId = id;
  }

  getRequestId() {
    return this.requestId;
  }
}

Когда нужен REQUEST scope:

  • Сохранение контекста текущего запроса (user ID, request ID)
  • Кеш только для текущего запроса
  • Трассировка уникальной информации

TRANSIENT Scope

@Injectable({ scope: Scope.TRANSIENT })
export class TransientService {
  private id = Math.random();

  getId() {
    return this.id;
  }
}

Каждая инъекция создаёт новый инстанс:

@Controller()
export class TestController {
  constructor(
    private service1: TransientService,
    private service2: TransientService,
  ) {}

  @Get('test')
  test() {
    // service1.getId() !== service2.getId() - разные инстансы
    return {
      service1: this.service1.getId(),
      service2: this.service2.getId(),
    };
  }
}

Когда использовать Singleton (по умолчанию)?

  • Сервисы работы с БД — DatabaseService должен быть один
  • Логирование — Logger должен быть один
  • Конфигурация — ConfigService должна быть одна
  • Кеширование — Cache должен быть один
  • Утилиты — UtilsService, StringService

Когда использовать REQUEST?

  • Контекст пользователя — сохранение текущего юзера
  • Трассировка запросов — уникальный ID для каждого запроса
  • Временные данные — данные только для одного запроса

Почему Singleton по умолчанию?

  1. Производительность — меньше создания новых инстансов
  2. Безопасность — в общих сервисах нет утечек данных между запросами
  3. Простота — разработчик не думает о scope, если не нужно

Типичная ошибка

// ❌ Плохо: сохранение пользователя в Singleton
@Injectable()
export class CurrentUserService {
  private currentUser: User; // Общий для всех запросов!

  setUser(user: User) {
    this.currentUser = user;
  }

  getUser() {
    return this.currentUser; // Утечка данных между пользователями
  }
}

// ✅ Хорошо: REQUEST scope для контекста пользователя
@Injectable({ scope: Scope.REQUEST })
export class CurrentUserService {
  private currentUser: User;
  // Каждый запрос получает свой инстанс
}

Da, Singleton в NestJS существует, это scope по умолчанию, и он используется в 90% случаев. REQUEST scope нужен только для контекстных данных текущего запроса.