Category Archives: inversify

inversify

I wanted to use an IoC pattern/library in a project I was working on (a node server application where I would slot in the specific implementations of the data and services at runtime).

I cam across inversify, there may be other IoC’s for TypeScript, but this is the one I looked into and worked a treat for me.

yarn add inversify reflect-metadata

You will need to enable experimentalDecorators and emitDecoratorMetadata within the tsconfig.json file.

"experimentalDecorators": true,        
"emitDecoratorMetadata": true, 

as per the invertsify documentation, we should also have the following tsconfig.json properties set

"target": "es5",
"lib": ["es6"],
"types": ["reflect-metadata"],
"module": "commonjs",
"moduleResolution": "node",

As is usual in C# (at least), the best way to work with IoC is to define an interface for our abstraction and then an implementation. However for inverisfy we also define a Symbol for the interfaces. So let’s assume we’re going to have a Service interface along with RestService and WebsocketService.

First create your Service.ts file and here’s a simple example

interface Service {
  call(data: string): void;
};

export default Service;

Now let’s create a couple of implementations, RestService.ts

import "reflect-metadata";
import Service from "./Service";
import { injectable } from "inversify";

@injectable()
class RestService implements Service {
  call(data: string): void {
    console.log("RestService called");
  }
}

export default RestService;

As you can see, we decorate the class with @injectable as this class may be injected into another class.

Here’s our WebsocketService.ts

import "reflect-metadata";
import Service from "./Service";
import { injectable } from "inversify";

@injectable()
class WebsocketService implements Service {
  call(data: string): void {
    console.log("WebsocketService called");
  }
}

export default WebsocketService;

Within the types.ts (below) we create the symbols (keys/identifiers) for use with our types.

const Types = {
  Service: Symbol.for("Service"),
  Application: Symbol.for("Application")
};

export default Types;

Here’s an Application interface Application.ts

interface Application {   
  run(): void;
}

export default Application;

MyApplication.ts

import "reflect-metadata";
import { inject, injectable } from "inversify";
import Application from "./Application";
import Types from "./types";
import Service from "./Service";

@injectable()
class MyApplication implements Application {
  private readonly service: Service;

  constructor(@inject(Types.Service) service) {
    this.service = service;
  }

  run(): void {        
    this.service.call("");
  }
}

export default MyApplication; 

In the above you can see we’re injecting (via the @inject decorator on the constructor) a type of symbol type Service. Hence this is where the server implementation will be injected into.

To supply inversify with the implementations for various types, we create the file inversify.config.ts. In the example file below we bind the symbol type/identifier with an implementation.

import { Container } from "inversify";
import Service from "./Service";
import RestService from "./RestService";
import Types from "./types";
import WebsocketService from "./WebsocketService";
import Application from "./Application";
import MyApplication from "./MyApplication";

const container = new Container();

//container.bind<Service>(Types.Service).to(RestService);
container.bind<Service>(Types.Service).to(WebsocketService);
container.bind<Application>(Types.Application).to(MyApplication);

export default container;

Finally, here’s an example implementation of the index.ts

import container from "./inversify.config";
import Application from "./Application";
import Types from "./types";

const application = container.get<Application>(Types.Application);
application.run();

There’s more to inversify than just the code listed above, such as you can have multiple Service implementations and use named bindings to receive the one you want and more.