Ответ
Да, в NestJS паттерн Singleton является основным и встроенным механизмом управления зависимостями (Dependency Injection). По умолчанию все провайдеры (помеченные декоратором @Injectable()), такие как сервисы, репозитории или кастомные провайдеры, регистрируются как синглтоны. Это означает, что на все время жизненного цикла приложения создается и повторно используется один единственный экземпляр класса.
Пример синглтона-сервиса:
// logger.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class LoggerService {
private logCount = 0;
log(message: string) {
this.logCount++;
console.log(`[#${this.logCount}] ${new Date().toISOString()}: ${message}`);
}
}
При инжекции этого сервиса в несколько контроллеров или других сервисов, они получат один и тот же экземпляр LoggerService, что видно по общему счетчику logCount.
// app.controller.ts
@Controller()
export class AppController {
constructor(private readonly logger: LoggerService) {}
@Get()
getHello() {
this.logger.log('Request to /'); // Счетчик увеличится для всех инстансов
return 'Hello';
}
}
Это поведение можно изменить, задав другой scope (область видимости):
Scope.REQUEST: новый экземпляр создается для каждого входящего HTTP-запроса.Scope.TRANSIENT: новый экземпляр создается для каждого потребителя (контроллера/сервиса), куда он инжектируется.
import { Injectable, Scope } from '@nestjs/common';
@Injectable({ scope: Scope.REQUEST })
export class RequestScopedService {} Ответ 18+ 🔞
А, блядь, про NestJS и синглтоны? Ну это же классика, ёпта! Тут всё просто, как три копейки, если не лезть в дебри.
Смотри, в этом вашем NestJS, если ты просто накинул @Injectable() на свой сервис или репозиторий, то он по умолчанию становится синглтоном. Это значит, что на всё приложение создаётся один-единственный экземпляр, и все остальные чуваки его и используют. Один на всех, как последняя пачка сигарет в армии. В рот меня чих-пых, удобно же! Не надо каждый раз новый объект городить, экономия памяти, да и состояние общее можно хранить.
Вот смотри на пример, тут всё понятно станет:
// logger.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class LoggerService {
private logCount = 0;
log(message: string) {
this.logCount++;
console.log(`[#${this.logCount}] ${new Date().toISOString()}: ${message}`);
}
}
Видишь этот счётчик logCount? Он теперь общий на всю банду. Куда бы ты этот LoggerService ни воткнул — в контроллер, в другой сервис, в хелпер — везде будет один и тот же объект. И счётчик будет накручиваться от всех обращений. Ни хуя себе, да? Удобная штука для счётчиков, кешей или подключений к базе.
// app.controller.ts
@Controller()
export class AppController {
constructor(private readonly logger: LoggerService) {}
@Get()
getHello() {
this.logger.log('Request to /'); // Этот вызов увеличит счётчик ВЕЗДЕ
return 'Hello';
}
}
Но, чувак, жизнь не всегда про синглтоны. Иногда нужно, чтобы для каждого запроса был свой свеженький экземпляр, или чтобы каждый потребитель получал свою отдельную копию. Подозрение ебать чувствую, что тебе это может понадобиться.
Для таких случаев есть scope (область видимости). Их, если по-простому, два основных:
Scope.REQUEST— тут ёперный театр начинается. Новый экземпляр сервиса создаётся для каждого нового HTTP-запроса. Запрос закончился — экземпляр на свалку истории. Идеально, когда нужно тащить за собой какие-то данные по всему конвейеру обработки одного запроса, не пачкая другие.Scope.TRANSIENT— а вот это уже манда с ушами. Здесь новый экземпляр создаётся для каждого класса, куда этот сервис инжектируется. Один сервис в двух контроллерах? Получи два разных объекта. Полная изоляция, никаких неожиданностей с общим состоянием.
Задаётся это вот так, просто и без затей:
import { Injectable, Scope } from '@nestjs/common';
@Injectable({ scope: Scope.REQUEST })
export class RequestScopedService {}
Вот и вся магия. По умолчанию — синглтон, живи да радуйся. Нужна изоляция — меняй scope. Главное, понимать, зачем ты это делаешь, а то можно такое впендюрить в архитектуру, что потом волосы дыбом встанут. Э, бошка, думай, прежде чем scope менять!