Skip to main content
Version: Next

Migrating from v6

InversifyJS v7 introduces a few breaking changes. This guide will help you migrate your existing InversifyJS v6 code to v7.

Container API

Autobinding

In v6, you could enable autobinding by passing the autoBindInjectable option to the container constructor. In v7, this option has been renamed to autobind which can be passed either as part of Container constructor options or Container.get options.

In v6, container.resolve would automatically bind the resolved service to the container. In v7, this behavior has been removed. You can enable this behavior by passing the autobind option.

export class Katana {
public readonly damage: number = 10;
}

@injectable()
export class Samurai {
public readonly katana: Katana;

constructor(katana: Katana) {
this.katana = katana;
}
}

const container: Container = new Container();

const samurai: Samurai = container.get(Samurai, { autobind: true });

Custom metadata and middlewares

This is the only feature that has been removed in v7 with no direct replacement. The API was not super useful, not widely used and was one of the main reasons for the complexity of the library. We might bring it back in the future with a better API.

isBound like methods

Container.isCurrentBound, Container.isBoundNamed, Container.isBoundTagged behavior can be achieved relying on Container.isBound with isBoundOptions.

get like methods

Container has been updated with no getNamed, getTagged, tryGet, tryGetNamed and tryGetTagged methods in favor of Container.get with OptionalGetOptions options.

The same applies to Container.getAll, Container.getAllAsync and Container.getAsync, they all now receive a GetOptions object which can be used to pass expected name or tags.

const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana).whenNamed('Katana');

const katana: Weapon = container.get<Weapon>('Weapon', { name: 'Katana' });

load and unload methods

This methods are now asynncronous and therefore return a promise.

const warriorsModule: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
options.bind<Ninja>('Ninja').to(Ninja);
},
);

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

await container.load(warriorsModule, weaponsModule);

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

unbind and unbindAll methods

Container.unbind and Container.unbindAll methods are now asynchronous and return a promise. Container.unbindAsync and Container.unbindAllAsync methods have been removed.

Parent and child containers

In v6, you could create a child container by calling the createChild method on a parent container. In v7, this method has been removed. Instead, you can create a child container by passing the parent container to the constructor of the child container.

class Katana {}

const parentContainer: Container = new Container();
parentContainer.bind(weaponIdentifier).to(Katana);

const childContainer: Container = new Container({ parent: parentContainer });

const katana: Katana = childContainer.get(weaponIdentifier);

ContainerModule API

Container module load options have been updated to be an object.

const warriorsModule: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
options.bind<Ninja>('Ninja').to(Ninja);
},
);

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

await container.load(warriorsModule, weaponsModule);

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

BindingFluentSyntax API

Use of ResolutionContext instead of interfaces.Context

In v6, the Context class was used to pass contextual information. Too many internal data structures were exposed to the user. In v7, the ResolutionContext class is used to pass contextual information. This class is more focused on the user's needs and hides internal data structures to keep the API simple and maintainable.

@injectable()
class Katana {
public use(): string {
return 'hit!';
}
}

container
.bind<Katana>('Katana')
.to(Katana)
.onActivation((_context: ResolutionContext, katana: Katana) => {
const handler: ProxyHandler<() => string> = {
apply: function (
target: () => string,
thisArgument: unknown,
argumentsList: [],
) {
console.log(`Starting: ${new Date().getTime().toString()}`);
const result: string = target.apply(thisArgument, argumentsList);
console.log(`Finished: ${new Date().getTime().toString()}`);
return result;
},
};

katana.use = new Proxy(katana.use.bind(katana), handler);

return katana;
});

Use of BindingMetadata instead of interfaces.Request

In v6, binding constraints received a Request object. In v7, binding constraints receive a BindingMetadata object. This object is more focused on the user's needs and hides internal data structures to keep the API simple and maintainable.

const ninjaId: symbol = Symbol.for('Ninja');
const weaponId: symbol = Symbol.for('Weapon');

@injectable()
class Ninja {
constructor(
@inject(weaponId)
@named('shuriken')
public readonly weapon: Weapon,
) {}
}

container.bind<Ninja>(ninjaId).to(Ninja);

const whenTargetNamedConstraint: (
name: string,
) => (bindingMetadata: BindingMetadata) => boolean =
(name: string) =>
(bindingMetadata: BindingMetadata): boolean =>
bindingMetadata.name === name;

container
.bind<Weapon>(weaponId)
.to(Katana)
.when(whenTargetNamedConstraint('katana'));

container
.bind<Weapon>(weaponId)
.to(Shuriken)
.when(whenTargetNamedConstraint('shuriken'));

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

// Returns 5
const ninjaDamage: number = ninja.weapon.damage;