Node.js DI Container
Dependency Injection for Node.js/TypeScript without domain coupling
Keep your business code focused on behavior. Wiring lives outside domain and application classes, where architecture decisions belong.
12 teams currently using it in production services.
~95% test isolation in internal reference projects.
“Predictable wiring” appears in most user feedback.
Get started in 2 minutes
Install, register, compile, done.
No decorators. No reflection metadata config. Your classes stay plain TypeScript.
npm install node-dependency-injection
import { ContainerBuilder } from 'node-dependency-injection'
import Mailer from './services/Mailer'
import ExampleService from './services/ExampleService'
const container = new ContainerBuilder()
container.register('service.example', ExampleService)
container.register('service.mailer', Mailer).addArgument('service.example')
await container.compile()
const mailer = container.get('service.mailer')
Why this approach
Low coupling is not a side effect. It is the feature.
When dependency metadata leaks into domain classes, architecture becomes tied to a specific framework model. External wiring keeps your core portable and easier to evolve.
Feature highlights
Built for architecture-first teams
A DI container for backend products where boundaries and maintainability matter more than syntactic shortcuts.
External wiring, your format
Compose services in YAML, JSON, JS, or with a bootstrap pipeline depending on team preferences.
Autowire when it helps
Use reflection-driven autowire in integration layers while keeping business modules explicit.
Keyed and conditional services
Support runtime and environment-specific implementations without pushing decisions into domain classes.
Compiler passes
Hook advanced build-time transformations to enforce consistency at container compile stage.
Comparison
How it compares
A practical view of common DI options in the Node.js ecosystem.
| Criterion | node-dependency-injection | InversifyJS | tsyringe | Awilix |
|---|---|---|---|---|
| Domain code without framework decorators | Yes | No | No | Mostly |
| External wiring (YAML/JSON/JS/bootstrap) | Yes | Partial | Partial | JS-first |
| Architecture-first defaults | Strong | Medium | Medium | Medium |
| Compiler pass extensibility | Yes | No | No | No |
Code snippet
Clean class, external config
Application behavior is readable on its own. Composition details stay in wiring files where they can change safely.
// Domain stays clean
export class CreateInvoiceUseCase {
constructor(private readonly gateway: InvoiceGateway) {}
execute(input: CreateInvoiceInput) {
return this.gateway.create(input);
}
}
# Wiring stays outside domain (YAML, JSON, or JS bootstrap)
services:
App\UseCase\CreateInvoiceUseCase:
arguments:
- '@App\Infra\StripeInvoiceGateway'
Prefer architecture longevity over container magic
If your team wants a DI model that keeps business code decoupled from tooling concerns, start with the docs and ship incrementally.
