Есть ли Singleton в NestJS?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Singleton в NestJS
Да, Singleton в NestJS существует и используется по умолчанию. Это один из трёх типов Scope (области видимости) инстансов в Nest.js фреймворке.
Три типа Scope в NestJS
- DEFAULT (Singleton) — один инстанс на всё приложение
- REQUEST — новый инстанс для каждого HTTP запроса
- 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 по умолчанию?
- Производительность — меньше создания новых инстансов
- Безопасность — в общих сервисах нет утечек данных между запросами
- Простота — разработчик не думает о 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 нужен только для контекстных данных текущего запроса.