Skip to main content

Middleware

warning

Middleware docs are included for historical reasons. They are likely to be remove in favor of more appropiate features.

Middlewares can be added to a container in order to intercept service resolution requests:

Basic middleware

import { interfaces, Container } from "inversify";

function logger(planAndResolve: interfaces.Next): interfaces.Next {
return (args: interfaces.NextArgs) => {
let start = new Date().getTime();
let result = planAndResolve(args);
let end = new Date().getTime();
console.log(`wooooo ${end - start}`);
return result;
};
}

let container = new Container();
container.applyMiddleware(logger);

Multiple middleware functions

When multiple middleware functions are applied:

container.applyMiddleware(middleware1, middleware2);

The middleware will be invoked from right to left. This means that middleware2 is invoked before middleware1.

Context interceptor

In some cases you may want to intercept the resolution plan.

The default contextInterceptor is passed to the middleware as an property of args.

function middleware1(planAndResolve: interfaces.Next): interfaces.Next<unknown> {
return (args: interfaces.NextArgs) => {
// args.nextContextInterceptor
// ...
};
}

You can extend the default contextInterceptor using a function:

function middleware1(planAndResolve: interfaces.Next<unknown>): interfaces.Next<unknown> {
return (args: interfaces.NextArgs) => {
let nextContextInterceptor = args.contextInterceptor;
args.contextInterceptor = (context: interfaces.Context) => {
console.log(context);
return nextContextInterceptor(context);
};
return planAndResolve(args);
};
}

Custom metadata reader

danger

It is not recommended to create your own custom metadata reader. We have included this feature to allow library / framework creators to have a higher level of customization but the average user should not use a custom metadata reader. In general, a custom metadata reader should only be used when developing a framework in order to provide users with an annotation APIs less explicit than the default annotation API.

If you are developing a framework or library and you create a custom metadata reader, Please remember to provide your framework with support for an alternative for all the decorators in the default API: @injectable, @inject, @multiInject, @tagged, @named, @optional, @postConstruct, @preDestroy @targetName & @unmanaged.

Middleware allows you to intercept a plan and resolve it but you are not allowed to change the way the annotation phase behaves.

There is a second extension point that allows you to decide what kind of annotation system you would like to use. The default annotation system is powered by decorators and reflect-metadata:

@injectable()
class Ninja implements Ninja {

private _katana: Katana;
private _shuriken: Shuriken;

constructor(
@inject("Katana") katana: Katana,
@inject("Shuriken") shuriken: Shuriken
) {
this._katana = katana;
this._shuriken = shuriken;
}

public fight() { return this._katana.hit(); };
public sneak() { return this._shuriken.throw(); };

}

You can use a custom metadata reader to implement a custom annotation system.

For example, you could implement an annotation system based on static properties:

class Ninja implements Ninja {

public static constructorInjections = [
"Katana", "Shuriken"
];

private _katana: Katana;
private _shuriken: Shuriken;

constructor(
katana: Katana,
shuriken: Shuriken
) {
this._katana = katana;
this._shuriken = shuriken;
}

public fight() { return this._katana.hit(); };
public sneak() { return this._shuriken.throw(); };

}

A custom metadata reader must implement the interfaces.MetadataReader interface.

A full example can be found in our unit tests.

Once you have a custom metadata reader you will be ready to apply it:

let container = new Container();
container.applyCustomMetadataReader(new StaticPropsMetadataReader());