Dependency Injection
Use NestJS 11's DI container for scopes, custom providers, and testable service wiring.
Recipe
Quick-reference recipe card - copy-paste ready.
import { Module, Injectable, Inject } from "@nestjs/common";
@Injectable()
export class UsersRepository {
findById(id: string) { return { id, name: "Ada" }; }
}
@Injectable()
export class UsersService {
constructor(private readonly repo: UsersRepository) {}
getUser(id: string) {
return this.repo.findById(id);
}
}
@Module({
providers: [UsersRepository, UsersService],
exports: [UsersService],
})
export class UsersModule {}When to reach for this: Any NestJS service that depends on another service, repository, or external client.
Working Example
import { Module, Injectable, Inject } from "@nestjs/common";
import { Test } from "@nestjs/testing";
// Interface token for swappable implementations
export const EMAIL_SENDER = Symbol("EMAIL_SENDER");
export interface EmailSender {
send(to: string, subject: string): Promise<void>;
}
@Injectable()
export class SmtpEmailSender implements EmailSender {
async send(to: string, subject: string) {
console.log(`Sending to ${to}: ${subject}`);
}
}
@Injectable()
export class UsersService {
constructor(
@Inject(EMAIL_SENDER) private readonly email: EmailSender,
) {}
async register(email: string) {
await this.email.send(email, "Welcome!");
return { email };
}
}
@Module({
providers: [
UsersService,
{ provide: EMAIL_SENDER, useClass: SmtpEmailSender },
],
})
export class UsersModule {}
// Test override
const testModule = await Test.createTestingModule({
imports: [UsersModule],
})
.overrideProvider(EMAIL_SENDER)
.useValue({ send: async () => {} })
.compile();What this demonstrates:
- Constructor injection of services
- Custom provider tokens with
Symbol useClassfor interface-to-implementation bindingoverrideProviderfor test doubles
Deep Dive
How It Works
- NestJS creates a DI container at bootstrap
@Injectable()registers a class as a provider- Constructor parameter types determine what to inject
- Container resolves dependency graph before serving requests
Provider Scopes
| Scope | Lifetime | Use for |
|---|---|---|
DEFAULT (singleton) | One instance per app | Services, repositories |
REQUEST | One per HTTP request | Request-scoped user context |
TRANSIENT | New instance per injection | Stateless helpers (rare) |
@Injectable({ scope: Scope.REQUEST })
export class RequestContextService {
userId?: string;
}Custom Provider Patterns
| Pattern | Syntax | Use for |
|---|---|---|
useClass | { provide: TOKEN, useClass: Impl } | Interface binding |
useValue | { provide: TOKEN, useValue: obj } | Config, mocks |
useFactory | { provide: TOKEN, useFactory: fn, inject: [...] } | Async setup |
useExisting | { provide: TOKEN, useExisting: OtherToken } | Aliasing |
Gotchas
- Circular dependencies - A injects B injects A. Fix:
forwardRef(() => B)or refactor shared logic to a third service. - Request-scoped in singleton - injecting request service into singleton fails. Fix: use
ModuleRefor make the consumer request-scoped too. - Forgetting
exports- service not available in importing module. Fix: add toexportsarray. - Manual
new Service()- bypasses DI and testability. Fix: always inject via constructor. - Too many providers in one module - god module anti-pattern. Fix: split into feature modules.
- Not overriding in tests - tests hit real database. Fix:
overrideProvideroruseValuemock.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Manual DI (constructor params) | Simple Fastify/Express app | NestJS project (use built-in DI) |
| tsyringe | DI without NestJS framework | Already on NestJS |
| Factory functions | Lightweight services | Complex dependency graphs |
| ModuleRef.get() | Dynamic provider resolution | Normal constructor injection works |
FAQs
How do I inject the ConfigService?
Import ConfigModule.forRoot({ isGlobal: true }) and inject ConfigService in any provider.
Can I inject request data in a service?
Use @Inject(REQUEST) with request-scoped provider, or pass data from controller to service method (simpler).
How do I share a provider across modules?
Add to providers and exports in the providing module. Import that module wherever needed.
What is forwardRef?
Delays resolution of a circular dependency. Use sparingly; refactoring is usually better.
How do I inject a database connection?
useFactory with onModuleDestroy cleanup, or use @nestjs/typeorm / PrismaService wrapper.
Does DI add performance overhead?
Minimal at runtime (singleton resolution once). Request scope adds per-request instantiation cost.
How do I test a controller with mocked service?
Test.createTestingModule({ controllers: [X], providers: [{ provide: Y, useValue: mock }] }).compile().
Can I use DI without decorators?
Not idiomatically in NestJS. Decorators are the framework's core mechanism.
Related
- NestJS Basics - module structure
- Guards, Interceptors & Pipes - injectable cross-cutting
- NestJS + Prisma/TypeORM - data layer DI
- Dependency Injection Patterns - framework-agnostic DI
- NestJS Best Practices - section checklist
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.