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.

Domain classes stay plain TypeScript, with no framework decorators.
Container rules are explicit and reviewable in dedicated config/bootstrap files.
You can switch infrastructure without rewriting application behavior.

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.

Criterionnode-dependency-injectionInversifyJStsyringeAwilix
Domain code without framework decoratorsYesNoNoMostly
External wiring (YAML/JSON/JS/bootstrap)YesPartialPartialJS-first
Architecture-first defaultsStrongMediumMediumMedium
Compiler pass extensibilityYesNoNoNo

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.