Dependency Injection in Typescript with Inversify

Photo Credit: processingly

When we start to use Typescript to program in Node.js, usually our main goal is make use of the benefits of object oriented languages, for instance, some of the design patterns.

The objective of this arcticle is to show a way to use one of these patterns: the dependency injection.

I’m not entering in details about the pattern itself but, very briefly, this pattern allows you to reduce the coupling of your source code by inverting the dependences of the modules and classes.

Let’s start by installing the necessary packages for it. I’m assuming you already have a project in typescript and you only want to add the injection in it.

npm i inversify^5.0.1 @types/inversify^2.0.33npm i reflect-metadata^0.1.13

If you need all the other packages as well as typescript, mongoose etc, the dependencies of my json.package are bellow.

“devDependencies”: {“@types/chai”: “⁴.2.7”,“@types/expect”: “²⁴.3.0”,“@types/lodash”: “⁴.14.149”,“@types/request”: “².48.4”,“@types/should”: “¹³.0.0”,“chai”: “⁴.2.0”,“request”: “².88.0”,“should”: “¹³.2.3”,“ts-node-dev”: “¹.0.0-pre.32”,“tslint”: “⁵.13.1”},“dependencies”: {“@types/body-parser”: “0.0.33”,“@types/dotenv”: “⁸.2.0”,“@types/express”: “⁴.16.1”,“@types/inversify”: “².0.33”,“@types/mongoose”: “⁵.5.41”,“@types/morgan”: “¹.7.32”,“@types/node”: “¹¹.11.3”,“@types/supertest”: “².0.8”,“body-parser”: “¹.15.2”,“dotenv”: “⁸.2.0”,“express”: “⁴.14.0”,“fs”: “0.0.1-security”,“install”: “⁰.13.0”,“inversify”: “⁵.0.1”,“lodash”: “⁴.17.15”,“mongoose”: “⁵.8.9”,“morgan”: “¹.7.0”,“morgan-body”: “².4.8”,“npm”: “⁶.13.6”,“reflect-metadata”: “⁰.1.13”,“supertest”: “⁴.0.2”,“typescript”: “³.7.4”}

Inversify is the inversion of control container. We’re gonna use it for register the bindings and resolve the dependencies. Read more at https://www.npmjs.com/package/inversify.

Reflect-metadata is required by executing some inversify functions. Read more about it at https://www.npmjs.com/package/reflect-metadata.

To inject a dependency, you have to mark the class as injectable, as shown bellow. Inversify is included in the imports, so we can use this function to “decorate” the class.

Code snippet of customer-routes.ts, with a class that defines costumer Api routes

ICustomerRepository, which is an interface, is passed by parameter in this class. Then, all of the concrete classes that implement this interface must be injectable too, once they will have to be injected.

i-customer-repository.ts, with the interface that will be implemented to be injected into other classes.

We have bellow pieces of codes of two implementations of ICustomerRepository. A mock class and a concrete repository class which executes mongoose operations. Notice that we have to include the injectable function to decorate both.

Code snippet of file customer-repository.ts. This class executes mongoose crud operations.
Mock class that implements the same interface.

Now we have to register the bindings, then the concrete classes that are passed on constructors we be properly injected. For this purpose I created a dependency injection container.

di-container.ts

This file contains the bindings for CustomerRoutes, Routes and ICustomerRepository, classes and repositories that are passed to constructors.

Notice that I registered ICustomerRepository implementations with the same name (it’s required) but you can give different names and differ them in the class that uses them (it’s useful for strategy pattern for example).

But in this case, as I’m working with mock/real data, I prefered to inject them optionally, controlling it with an environment variable. You can use this approach to mock your data before creating a real database.

Finally, to execute the injection, my index.ts file, that is the one who calls the register of middlewares, routes etc, will call the resolve() method, resolving the first dependency, which is Routes. Notice that reflect-metadata is include in this class.

index.ts
app-bootstrap.ts. I prefered to separate this class to register middlewares, database connection and set routes. Notice that Routes, which was registered in di-container, is a parameter to it’s constructor.

Having done that, when you serve you project, all the dependencies will be resolved.

That’s all folks, I hope it will be useful for you. See you!

Software Engineer. Guitarrist. Frequent Google and Medium goer.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store