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-use-interfaces-tokens.md
1---2title: Use Injection Tokens for Interfaces3impact: HIGH4impactDescription: Enables interface-based DI at runtime5tags: dependency-injection, tokens, interfaces6---78## Use Injection Tokens for Interfaces910TypeScript interfaces are erased at compile time and can't be used as injection tokens. Use string tokens, symbols, or abstract classes when you want to inject implementations of interfaces. This enables swapping implementations for testing or different environments.1112**Incorrect (interface can't be used as token):**1314```typescript15// Interface can't be used as injection token16interface PaymentGateway {17charge(amount: number): Promise<PaymentResult>;18}1920@Injectable()21export class StripeService implements PaymentGateway {22charge(amount: number) { /* ... */ }23}2425@Injectable()26export class OrdersService {27// This WON'T work - PaymentGateway doesn't exist at runtime28constructor(private payment: PaymentGateway) {}29}30```3132**Correct (symbol tokens or abstract classes):**3334```typescript35// Option 1: String/Symbol tokens (most flexible)36export const PAYMENT_GATEWAY = Symbol('PAYMENT_GATEWAY');3738export interface PaymentGateway {39charge(amount: number): Promise<PaymentResult>;40}4142@Injectable()43export class StripeService implements PaymentGateway {44async charge(amount: number): Promise<PaymentResult> {45// Stripe implementation46}47}4849@Injectable()50export class MockPaymentService implements PaymentGateway {51async charge(amount: number): Promise<PaymentResult> {52return { success: true, id: 'mock-id' };53}54}5556// Module registration57@Module({58providers: [59{60provide: PAYMENT_GATEWAY,61useClass: process.env.NODE_ENV === 'test'62? MockPaymentService63: StripeService,64},65],66exports: [PAYMENT_GATEWAY],67})68export class PaymentModule {}6970// Injection71@Injectable()72export class OrdersService {73constructor(74@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway,75) {}7677async createOrder(dto: CreateOrderDto) {78await this.payment.charge(dto.amount);79}80}8182// Option 2: Abstract class (carries runtime type info)83export abstract class PaymentGateway {84abstract charge(amount: number): Promise<PaymentResult>;85}8687@Injectable()88export class StripeService extends PaymentGateway {89async charge(amount: number): Promise<PaymentResult> {90// Implementation91}92}9394// No @Inject needed with abstract class95@Injectable()96export class OrdersService {97constructor(private payment: PaymentGateway) {}98}99```100101Reference: [NestJS Custom Providers](https://docs.nestjs.com/fundamentals/custom-providers)102