Skip to main content
Practice Problems

What are providers and how does dependency injection work in NestJS?

Providers and Dependency Injection in NestJS

Providers are a core concept in NestJS. Services, repositories, factories, helpers โ€” anything decorated with @Injectable() can be a provider. The NestJS IoC container manages their instantiation and injection automatically.


Services (Most Common Provider)

typescript
// users/users.service.ts import { Injectable, NotFoundException } from '@nestjs/common'; @Injectable() export class UsersService { private users = [ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, ]; findAll() { return this.users; } findOne(id: number) { const user = this.users.find(u => u.id === id); if (!user) throw new NotFoundException(`User #${id} not found`); return user; } create(data: { name: string }) { const user = { id: Date.now(), ...data }; this.users.push(user); return user; } }

Injecting a Service into a Controller

typescript
@Controller('users') export class UsersController { // NestJS injects UsersService automatically via the constructor constructor(private readonly usersService: UsersService) {} @Get() findAll() { return this.usersService.findAll(); } }

NestJS reads the TypeScript type UsersService and resolves it from the module's provider registry.


Provider Scopes

typescript
import { Injectable, Scope } from '@nestjs/common'; @Injectable({ scope: Scope.DEFAULT }) // Singleton (default) โ€” one instance @Injectable({ scope: Scope.REQUEST }) // New instance per request @Injectable({ scope: Scope.TRANSIENT }) // New instance per injection export class UsersService {}

Custom Providers

useValue โ€” Inject a constant

typescript
@Module({ providers: [ { provide: 'API_KEY', useValue: 'my-secret-key' }, ], }) export class AppModule {} // Inject with @Inject() @Injectable() export class ApiService { constructor(@Inject('API_KEY') private apiKey: string) {} }

useClass โ€” Override with a different class

typescript
@Module({ providers: [ { provide: LoggerService, useClass: MockLoggerService }, ], })

useFactory โ€” Dynamic provider

typescript
@Module({ providers: [ { provide: 'DATABASE', useFactory: async (config: ConfigService) => { return await createDatabaseConnection(config.get('DB_URL')); }, inject: [ConfigService], }, ], })

useExisting โ€” Alias provider

typescript
{ provide: 'LoggerAlias', useExisting: LoggerService }

Injecting Between Services

typescript
@Injectable() export class PostsService { constructor( private readonly usersService: UsersService, // injected automatically private readonly emailService: EmailService, ) {} async createPost(userId: number, data: CreatePostDto) { const user = await this.usersService.findOne(userId); await this.emailService.sendNotification(user.email); // ... } }

Summary

NestJS's DI container works by:

  1. Scanning @Injectable() classes registered in module providers
  2. Reading constructor parameter types (TypeScript reflection)
  3. Resolving and injecting the correct instances

This enables loose coupling, easy unit testing (mock injections), and lifecycle management (singletons, request-scoped, etc.).

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems