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.