advanced
NestJS dependency injection
Explain provider tokens, constructor injection, module visibility, request scope, custom providers, overrides, and test seams.
NestJS builds a dependency graph at bootstrap: constructor parameters with type metadata resolve to registered providers. Modules control visibility — a provider is injectable only where its module is imported and the provider is exported.
const MAILER = Symbol('MAILER');
@Module({
providers: [
{ provide: MAILER, useClass: SmtpMailer },
{ provide: UsersService, useClass: UsersService },
],
exports: [UsersService, MAILER],
})
export class UsersModule {}
`@Inject(TOKEN)` binds interfaces and custom tokens. `forwardRef` breaks circular constructor dependencies. Testing overrides providers via `TestingModule.overrideProvider`.
On interviews: contrast Nest DI with manual singletons, explain module visibility rules, and how request-scoped injection propagates to dependent providers.
Common pitfalls: injecting concrete classes where interfaces should be tokenized, circular module graphs, and assuming global availability without `exports`.
The trade-off is balancing simplicity, performance, safety, and operability — name which axis you optimized and what cost you accepted.
Checklist:
- Constructor injection over property injection.
- Tokenize ports and adapters for test seams.
- Export providers explicitly across modules.
- Override providers in tests, not production hacks.