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:
- Scanning
@Injectable()classes registered in moduleproviders - Reading constructor parameter types (TypeScript reflection)
- 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 readyPremium
A concise answer to help you respond confidently on this topic during an interview.