InversifyJS IoC Container
InversifyJS is a robust and lightweight Inversion of Control (IoC) container for JavaScript and TypeScript applications, currently at version 8.1.0. It facilitates Dependency Injection (DI) by using decorators and TypeScript's reflection capabilities to manage the instantiation and injection of dependencies, promoting adherence to SOLID principles, good OOP, and IoC practices. The library is actively maintained with frequent minor and patch releases across its core and ecosystem packages, addressing new features and bug fixes. Key differentiators include its strong TypeScript integration, minimal runtime overhead, and a developer-friendly API designed to enhance modularity, testability, and maintainability. InversifyJS emphasizes a explicit dependency graph, helping developers build scalable applications by reducing coupling between components. It relies heavily on `reflect-metadata` for design-time type information, which is a fundamental aspect of its operation.
Common errors
-
TypeError: Reflect.hasOwnMetadata is not a function
cause The `reflect-metadata` polyfill was not imported or was imported too late.fixAdd `import 'reflect-metadata';` as the very first line of your application's entry file. Ensure `reflect-metadata` is installed as a dependency. Check `tsconfig.json` for `"emitDecoratorMetadata": true` and `"experimentalDecorators": true`. -
No matching bindings found for serviceIdentifier: Symbol(MyService)
cause A dependency was requested from the container but no `container.bind()` call exists for that `ServiceIdentifier` (e.g., `Symbol`, string, or class).fixEnsure `container.bind<T>(TYPES.MyService).to(MyServiceImpl);` is called before attempting to `container.get<T>(TYPES.MyService);`. Double-check the `ServiceIdentifier` used in `bind` and `get` matches exactly. -
Maximum call stack size exceeded
cause This typically indicates a circular dependency between services where Service A depends on Service B, and Service B directly or indirectly depends back on Service A, causing an infinite loop during instantiation.fixIdentify the circular dependency. Strategies to resolve include: refactoring to break the cycle, using an initializer method (`@postConstruct`), using `toDynamicValue` or `toFactory` for lazy loading, or introducing an intermediate interface/abstraction. -
ReferenceError: Container is not defined
cause The `Container` class was used without being imported or without its module being properly transpiled/resolved.fixAdd `import { Container } from 'inversify';` to the top of your file. If using CommonJS in an environment that doesn't support `require(esm)` (e.g., Node.js < 20.19.0), you might need to use dynamic imports or transpile your code to a compatible module format.
Warnings
- breaking InversifyJS v8 introduced a 'sync-first' naming convention. Methods like `container.load`, `unbind`, `rebind`, and `unload` are now synchronous by default. Their asynchronous counterparts are suffixed with `Async` (e.g., `container.loadAsync`). This reverses the behavior introduced in v7, where these methods were asynchronous by default.
- breaking InversifyJS v8 is an ESM-only package and no longer provides a CommonJS build. While Node.js 20.19.0 and later (or Node.js 22+) support `require()` for ESM packages, older Node.js versions (e.g., 18 or earlier, which reached EOL in April 2025) will require projects to either upgrade Node.js or switch to dynamic `import('inversify')` where acceptable.
- breaking The `ServiceIdentifier<T>` type in v8 no longer accepts an arbitrary `Function` but strictly requires `Newable` or `AbstractNewable`. This primarily impacts using classes with `protected` or `private` constructors as service identifiers, where a `Symbol` identifier should now be used instead.
- breaking The `toProvider` binding method and the `Provider` type have been completely removed in InversifyJS v8. The `Factory` pattern (introduced in v7) covers all previous use cases of `Provider` with greater flexibility and a cleaner API.
- gotcha InversifyJS critically depends on `reflect-metadata` for its decorator-based dependency injection. It must be installed (`npm install reflect-metadata`) and imported once at the very top of your application's entry file: `import 'reflect-metadata';`. Failure to do so will result in runtime errors like 'Reflect is not defined' or 'TypeError: Reflect.hasOwnMetadata is not a function'.
- breaking InversifyJS v7 deprecated implicit injection inheritance in favor of the `@injectFromBase` decorator. In v8, custom metadata and middlewares were removed entirely with no direct replacement, contributing to library simplification.
Install
-
npm install inversify -
yarn add inversify -
pnpm add inversify
Imports
- Container
const { Container } = require('inversify');import { Container } from 'inversify'; - @injectable
import { Injectable } from 'inversify';import { injectable } from 'inversify'; - @inject
import { Inject } from 'inversify';import { inject } from 'inversify'; - TYPES
import { TYPES } from './types'; - reflect-metadata polyfill
import 'reflect-metadata';
Quickstart
import 'reflect-metadata';
import { Container, injectable, inject } from 'inversify';
// 1. Define interfaces (abstractions)
interface Warrior {
fight(): string;
sneak(): string;
}
interface Weapon {
hit(): string;
}
interface ThrowableWeapon {
throw(): string;
}
// 2. Define binding identifiers (symbols or classes)
const TYPES = {
Warrior: Symbol.for('Warrior'),
Weapon: Symbol.for('Weapon'),
ThrowableWeapon: Symbol.for('ThrowableWeapon'),
};
// 3. Implement concrete classes and use decorators
@injectable()
class Katana implements Weapon {
public hit(): string {
return 'cut!';
}
}
@injectable()
class Shuriken implements ThrowableWeapon {
public throw(): string {
return 'hit!';
}
}
@injectable()
class Ninja implements Warrior {
private _katana: Weapon;
private _shuriken: ThrowableWeapon;
public constructor(
@inject(TYPES.Weapon) katana: Weapon,
@inject(TYPES.ThrowableWeapon) shuriken: ThrowableWeapon,
) {
this._katana = katana;
this._shuriken = shuriken;
}
public fight(): string {
return this._katana.hit();
}
public sneak(): string {
return this._shuriken.throw();
}
}
// 4. Create and configure a container
const myContainer = new Container();
myContainer.bind<Warrior>(TYPES.Warrior).to(Ninja);
myContainer.bind<Weapon>(TYPES.Weapon).to(Katana);
myContainer.bind<ThrowableWeapon>(TYPES.ThrowableWeapon).to(Shuriken);
// 5. Resolve dependencies
const ninja = myContainer.get<Warrior>(TYPES.Warrior);
console.log(ninja.fight()); // Expected output: cut!
console.log(ninja.sneak()); // Expected output: hit!