π Introduction: Dependency Injection lets NestJS create and provide dependencies instead of you manually instantiating them.
@Injectable()export class UsersService {findAll() {return ['user1', 'user2'];}}@Controller('users')export class UsersController {constructor(private usersService: UsersService) {}@Get()getUsers() {return this.usersService.findAll();}}
π Explanation: NestJS automatically creates UsersService and injects it into the controller.
π Introduction: Modules control which providers are private or shared across the app.
@Module({providers: [UsersService],exports: [UsersService],})export class UsersModule {}@Module({imports: [UsersModule],providers: [OrdersService],})export class OrdersModule {}
π Explanation: UsersService is accessible in OrdersModule because it is exported.
π Introduction: Custom providers allow you to define exactly how a dependency is created.
@Module({providers: [{provide: 'HELLO_MESSAGE',useValue: 'Hello DI',},],})export class AppModule {}
@Injectable()export class HelloService {constructor(@Inject('HELLO_MESSAGE') private message: string) {}getMessage() {return this.message;}}
π Explanation: You manually define what gets injected using a custom token.
useValue)π Introduction: Value providers inject static data into your application.
@Module({providers: [{provide: 'CONFIG',useValue: {defaultLimit: 10,},},],})export class AppModule {}
@Injectable()export class UsersService {constructor(@Inject('CONFIG') private config) {}getLimit() {return this.config.defaultLimit;}}
π Explanation: Useful for config or constants without logic.
π Introduction: Tokens donβt have to be classesβthey can be strings or symbols.
export const CONFIG_TOKEN = Symbol('CONFIG');@Module({providers: [{provide: CONFIG_TOKEN,useValue: { limit: 20 },},],})export class AppModule {}
@Injectable()export class UsersService {constructor(@Inject(CONFIG_TOKEN) private config) {}}
π Explanation: Symbols prevent naming conflicts in large apps.
useClass)π Introduction: Class providers let you swap implementations for the same dependency.
@Injectable()export class MockUsersService {findAll() {return ['mock-user'];}}
@Module({providers: [{provide: UsersService,useClass: MockUsersService,},],})export class AppModule {}
@Controller('users')export class UsersController {constructor(private usersService: UsersService) {}@Get()getUsers() {return this.usersService.findAll();}}
π Explanation: Controller still injects UsersService, but gets MockUsersService.
useFactory)π Introduction: Factory providers create dependencies using a function.
@Module({providers: [{provide: 'RANDOM_NUMBER',useFactory: () => Math.random(),},],})export class AppModule {}
@Injectable()export class RandomService {constructor(@Inject('RANDOM_NUMBER') private num: number) {}getNumber() {return this.num;}}
π Explanation: The value is generated dynamically when injected.
π Introduction: Async providers allow dependencies to be initialized asynchronously.
@Module({providers: [{provide: 'ASYNC_DATA',useFactory: async () => {return new Promise((resolve) =>setTimeout(() => resolve('Loaded'), 1000),);},},],})export class AppModule {}
@Injectable()export class DataService {constructor(@Inject('ASYNC_DATA') private data: string) {}}
π Explanation: Nest waits for async data before starting the app.
π Introduction: Dynamic modules allow you to configure a module when importing it.
@Module({})export class ConfigModule {static forRoot(options): DynamicModule {return {module: ConfigModule,providers: [{ provide: 'CONFIG', useValue: options },],exports: ['CONFIG'],};}}
@Module({imports: [ConfigModule.forRoot({ port: 3000 })],})export class AppModule {}
@Injectable()export class AppService {constructor(@Inject('CONFIG') private config) {}}
π Explanation: Config is passed at import time and injected anywhere.
π Introduction: Request-scoped providers create a new instance for each request.
@Injectable({ scope: Scope.REQUEST })export class RequestContextService {constructor(@Inject(REQUEST) private req: Request) {}getUser() {return this.req.user;}}
@Injectable()export class UsersService {constructor(private ctx: RequestContextService) {}getCurrentUser() {return this.ctx.getUser();}}
π Explanation: Each request gets its own instance, allowing access to request-specific data.
Dependency Injection in NestJS is the foundation of:
Master it, and you move from writing code β designing systems.