Node.js DI Container
Dependency Injection for TypeScript without decorators or reflect metadata
Solve real backend wiring pain: autowire services, validate dependencies, compile your container, and keep domain code clean while teams scale.
Autowire built for TypeScript with interface-aware resolution patterns.
Compile-time container checks before runtime surprises.
CLI + compiler passes for large project workflows.
The 30-second test
If this feels obvious, the container is doing its job
Plain classes in your domain. Strong guarantees in your container pipeline.
class UserService {
constructor(
private readonly repository: UserRepository,
private readonly logger: Logger
) {}
}
const userService = await container.get(UserService)
What you get immediately
- OKDependencies validated
- OKInterfaces resolved
- OKNo decorators
- OKNo runtime reflection
Get started in 2 minutes
Install, wire, validate, compile.
Your classes stay plain TypeScript while the container handles wiring guarantees.
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
Why teams choose node-dependency-injection
When evaluating Awilix, tsyringe, and Inversify, this is where the differences show up in real projects.
| Criterion | node-dependency-injection | InversifyJS | tsyringe | Awilix |
|---|---|---|---|---|
| Requires decorators | No | Yes | Yes | No |
| Requires reflect-metadata | No | Yes | Yes | No |
| Container compilation | Yes | No | No | No |
| Container validation | Yes | Partial | Partial | Partial |
| CLI | Yes | No | No | No |
| Compiler passes | Yes | No | No | No |
Code snippet
Clean class, explicit wiring
Application behavior remains readable on its own. Composition details stay in wiring files where they can evolve 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.
