Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
40 prioritized NestJS best practices across architecture, DI, security, performance, testing, and microservices.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
rules/di-scope-awareness.md
1---2title: Understand Provider Scopes3impact: CRITICAL4impactDescription: Prevents data leaks and performance issues5tags: dependency-injection, scopes, request-context6---78## Understand Provider Scopes910NestJS has three provider scopes: DEFAULT (singleton), REQUEST (per-request instance), and TRANSIENT (new instance for each injection). Most providers should be singletons. Request-scoped providers have performance implications as they bubble up through the dependency tree. Understanding scopes prevents memory leaks and incorrect data sharing.1112**Incorrect (wrong scope usage):**1314```typescript15// Request-scoped when not needed (performance hit)16@Injectable({ scope: Scope.REQUEST })17export class UsersService {18// This creates a new instance for EVERY request19// All dependencies also become request-scoped20async findAll() {21return this.userRepo.find();22}23}2425// Singleton with mutable request state26@Injectable() // Default: singleton27export class RequestContextService {28private userId: string; // DANGER: Shared across all requests!2930setUser(userId: string) {31this.userId = userId; // Overwrites for all concurrent requests32}3334getUser() {35return this.userId; // Returns wrong user!36}37}38```3940**Correct (appropriate scope for each use case):**4142```typescript43// Singleton for stateless services (default, most common)44@Injectable()45export class UsersService {46constructor(private readonly userRepo: UserRepository) {}4748async findById(id: string): Promise<User> {49return this.userRepo.findOne({ where: { id } });50}51}5253// Request-scoped ONLY when you need request context54@Injectable({ scope: Scope.REQUEST })55export class RequestContextService {56private userId: string;5758setUser(userId: string) {59this.userId = userId;60}6162getUser(): string {63return this.userId;64}65}6667// Better: Use NestJS built-in request context68import { REQUEST } from '@nestjs/core';69import { Request } from 'express';7071@Injectable({ scope: Scope.REQUEST })72export class AuditService {73constructor(@Inject(REQUEST) private request: Request) {}7475log(action: string) {76console.log(`User ${this.request.user?.id} performed ${action}`);77}78}7980// Best: Use ClsModule for async context (no scope bubble-up)81import { ClsService } from 'nestjs-cls';8283@Injectable() // Stays singleton!84export class AuditService {85constructor(private cls: ClsService) {}8687log(action: string) {88const userId = this.cls.get('userId');89console.log(`User ${userId} performed ${action}`);90}91}92```9394Reference: [NestJS Injection Scopes](https://docs.nestjs.com/fundamentals/injection-scopes)95