Skip to main content
Version: Next

Migrating from v7

InversifyJS v8 introduces several breaking changes from v7. This guide will help you migrate your existing InversifyJS v7 code to v8.

Overview of Changes

The following table summarizes the key changes from v7 to v8:

v7v8Comment
container.load(...)container.loadAsync(...)Async load renamed to loadAsync
container.loadSync(...)container.load(...)Sync load becomes the default load
container.unbind(...)container.unbindAsync(...)Async unbind renamed to unbindAsync
container.unbindSync(...)container.unbind(...)Sync unbind becomes the default unbind
container.unbindAll()container.unbindAllAsync()Async unbindAll renamed to unbindAllAsync
container.unbindAllSync()container.unbindAll()Sync unbindAll becomes the default unbindAll
container.rebind(...)container.rebindAsync(...)Async rebind renamed to rebindAsync
container.rebindSync(...)container.rebind(...)Sync rebind becomes the default rebind
container.unload(...)container.unloadAsync(...)Async unload renamed to unloadAsync
container.unloadSync(...)container.unload(...)Sync unload becomes the default unload
options.unbind (async)options.unbindAsyncContainerModuleLoadOptions async unbind renamed
options.unbindSyncoptions.unbindContainerModuleLoadOptions sync unbind becomes default
options.rebind (async)options.rebindAsyncContainerModuleLoadOptions async rebind renamed
options.rebindSyncoptions.rebindContainerModuleLoadOptions sync rebind becomes default
ServiceIdentifier accepts FunctionServiceIdentifier requires Newable or AbstractNewableStricter type for class-based service identifiers
.toProvider(...).toFactory(...) with async functionProvider type and toProvider binding removed; use toFactory instead
CJS/ESM dual packageESM onlyPackage is now ESM only; Node.js LTS supports require(esm)

Package Format

InversifyJS v8 is now an ESM-only package. For most users this is a non-breaking change: all active Node.js LTS versions (20.x and above) support require(esm), meaning CommonJS projects can still consume the package without any changes.

Refer to the Migrating the ecosystem to ES modules blog post for a broader overview of the CJS → ESM migration landscape.

Container API

Method Renames: Sync-First Convention

The most significant change in v8 is the adoption of a sync-first naming convention. In v7, the default method names (e.g., load, unbind, rebind, unload) were asynchronous and returned promises, while synchronous variants had a Sync suffix. In v8, this is reversed: the default names are now synchronous, and asynchronous variants have an Async suffix.

load / loadAsync

In v7, container.load was asynchronous. In v8, the synchronous version is the default:

v7:

const weaponsModule: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
options.bind<Katana>('Weapon').to(Katana).whenNamed('Melee');
},
);

// v7: async load (default)
await container.load(weaponsModule);

container.bind(Ninja).toSelf();

const ninja: Ninja = container.get(Ninja);

v8:

const weaponsModule: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
options.bind<Katana>('Weapon').to(Katana).whenNamed('Melee');
},
);

// v8: sync load (default)
container.load(weaponsModule);

container.bind(Ninja).toSelf();

const ninja: Ninja = container.get(Ninja);

unbind / unbindAsync

In v7, container.unbindSync was the synchronous variant. In v8, it becomes the default unbind:

v7:

const container: Container = new Container();

container.bind<Weapon>('Weapon').to(Katana);

// v7: sync unbind
container.unbindSync('Weapon');

const isBound: boolean = container.isBound('Weapon');

v8:

const container: Container = new Container();

container.bind<Weapon>('Weapon').to(Katana);

// v8: sync unbind (default)
container.unbind('Weapon');

const isBound: boolean = container.isBound('Weapon');

unbindAll / unbindAllAsync

In v7, container.unbindAllSync was the synchronous variant. In v8, it becomes the default unbindAll:

v7:

const container: Container = new Container();

container.bind<Weapon>('Weapon').to(Katana);

// v7: sync unbindAll
container.unbindAllSync();

const isBound: boolean = container.isBound('Weapon');

v8:

const container: Container = new Container();

container.bind<Weapon>('Weapon').to(Katana);

// v8: sync unbindAll (default)
container.unbindAll();

const isBound: boolean = container.isBound('Weapon');

rebind / rebindAsync

In v7, container.rebindSync was the synchronous variant. In v8, it becomes the default rebind:

v7:

const container: Container = new Container();

container.bind<Weapon>('Weapon').to(Katana);

// v7: sync rebind
container.rebindSync<Weapon>('Weapon').to(Shuriken);

const weapon: Weapon = container.get<Weapon>('Weapon');

v8:

const container: Container = new Container();

container.bind<Weapon>('Weapon').to(Katana);

// v8: sync rebind (default)
container.rebind<Weapon>('Weapon').to(Shuriken);

const weapon: Weapon = container.get<Weapon>('Weapon');

unload / unloadAsync

In v7, container.unloadSync was the synchronous variant. In v8, it becomes the default unload:

v7:

const weaponsModule: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
options.bind<Katana>('Weapon').to(Katana);
},
);

await container.load(weaponsModule);

// v7: sync unload
container.unloadSync(weaponsModule);

const isBound: boolean = container.isBound('Weapon');

v8:

const weaponsModule: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
options.bind<Katana>('Weapon').to(Katana);
},
);

container.load(weaponsModule);

// v8: sync unload (default)
container.unload(weaponsModule);

export const isBound: boolean = container.isBound('Weapon');
tip

If your codebase predominantly uses the synchronous variants (e.g., loadSync, unbindSync, rebindSync, unloadSync), the migration is straightforward: simply remove the Sync suffix.

If you mainly use the async variants, rename them by adding the Async suffix (e.g., loadloadAsync, unbindunbindAsync).

ContainerModuleLoadOptions

The same sync-first convention applies to ContainerModuleLoadOptions:

v7:

const module: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
// v7: sync unbind and rebind
options.unbindSync('Weapon');
options.bind<Weapon>('Weapon').to(Shuriken);
},
);

await container.load(module);

v8:

const module: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
// v8: sync unbind and rebind (default)
options.unbind('Weapon');
options.bind<Weapon>('Weapon').to(Shuriken);
},
);

container.load(module);

ServiceIdentifier Type

In v7, ServiceIdentifier accepted any Function as a valid service identifier. In v8, the type has been tightened: a Function is only valid if it satisfies Newable<T> or AbstractNewable<T>. This provides better type safety and ensures that only constructable classes can be used as class-based service identifiers.

// v7 — any function was accepted
type ServiceIdentifier<T> = string | symbol | Newable<T> | Function;

// v8 — only constructable classes are accepted
type ServiceIdentifier<T> = string | symbol | Newable<T> | AbstractNewable<T>;

In practice, this change is transparent for most codebases since service identifiers are typically classes, strings, or symbols. If you were using a plain function as a service identifier, you will need to switch to a string or symbol identifier instead.

Removed toProvider Binding and Provider Type

The toProvider binding method and the Provider type have been removed in v8. Providers were factories that returned a Promise<T>, so the same behavior can be achieved with toFactory using an async function:

v7:

export const container: Container = new Container();

container.bind<Sword>('Sword').to(Katana);

// v7: toProvider (deprecated)
container
.bind<SwordProvider>('SwordProvider')
.toProvider((context: ResolutionContext) => {
return async (material: string, damage: number): Promise<Sword> => {
return new Promise<Sword>(
(resolve: (value: Sword | PromiseLike<Sword>) => void) => {
setTimeout(() => {
const sword: Sword = context.get<Sword>('Sword');
sword.material = material;
sword.damage = damage;
resolve(sword);
}, 10);
},
);
};
});

v8:

export const container: Container = new Container();

container.bind<Sword>('Sword').to(Katana);

// v8: use toFactory with async function instead of toProvider
container
.bind<SwordProvider>('SwordProvider')
.toFactory((context: ResolutionContext) => {
return async (material: string, damage: number): Promise<Sword> => {
return new Promise<Sword>(
(resolve: (value: Sword | PromiseLike<Sword>) => void) => {
setTimeout(() => {
const sword: Sword = context.get<Sword>('Sword');
sword.material = material;
sword.damage = damage;
resolve(sword);
}, 10);
},
);
};
});

Quick Migration Checklist

  1. Update async method calls: Rename loadloadAsync, unbindunbindAsync, rebindrebindAsync, unloadunloadAsync, unbindAllunbindAllAsync where you use the async versions.
  2. Update sync method calls: Remove the Sync suffix from loadSync, unbindSync, rebindSync, unloadSync, unbindAllSync.
  3. Update ContainerModuleLoadOptions usage: Apply the same renaming for unbind/unbindSyncunbindAsync/unbind and rebind/rebindSyncrebindAsync/rebind.
  4. Replace toProvider with toFactory: Use toFactory with an async function instead.
  5. Remove Provider type imports: Replace with a custom type or use Factory instead.
  6. Check ServiceIdentifier usage: Ensure you are not using plain functions as service identifiers.
  7. Verify ESM compatibility: Ensure your project can consume ESM packages (Node.js 20.x+ supports require(esm)).