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:
| v7 | v8 | Comment |
|---|---|---|
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.unbindAsync | ContainerModuleLoadOptions async unbind renamed |
options.unbindSync | options.unbind | ContainerModuleLoadOptions sync unbind becomes default |
options.rebind (async) | options.rebindAsync | ContainerModuleLoadOptions async rebind renamed |
options.rebindSync | options.rebind | ContainerModuleLoadOptions sync rebind becomes default |
ServiceIdentifier accepts Function | ServiceIdentifier requires Newable or AbstractNewable | Stricter type for class-based service identifiers |
.toProvider(...) | .toFactory(...) with async function | Provider type and toProvider binding removed; use toFactory instead |
| CJS/ESM dual package | ESM only | Package 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');
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., load → loadAsync, unbind → unbindAsync).
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
- Update async method calls: Rename
load→loadAsync,unbind→unbindAsync,rebind→rebindAsync,unload→unloadAsync,unbindAll→unbindAllAsyncwhere you use the async versions. - Update sync method calls: Remove the
Syncsuffix fromloadSync,unbindSync,rebindSync,unloadSync,unbindAllSync. - Update
ContainerModuleLoadOptionsusage: Apply the same renaming forunbind/unbindSync→unbindAsync/unbindandrebind/rebindSync→rebindAsync/rebind. - Replace
toProviderwithtoFactory: UsetoFactorywith an async function instead. - Remove
Providertype imports: Replace with a custom type or useFactoryinstead. - Check
ServiceIdentifierusage: Ensure you are not using plain functions as service identifiers. - Verify ESM compatibility: Ensure your project can consume ESM packages (Node.js 20.x+ supports
require(esm)).