Dependency Injection in TypeScript
By: Mert Türkmenoğlu
Tags: Design Patterns, Typedi, Nodejs, Typescript, Dependency Injection

Using TypeDI

Once we understand the underlying principles, what framework or library we use makes no difference. For this article, I chose TypeScript language and TypeDI library to demonstrate the fundamental concepts.

Initializing Yarn and adding TypeScript takes a little bit of effort. Because I don’t want to bore you with well-known project configs with no additional information, I will give you the starting code and go through it briefly. You can view and download the codes from this GitHub repository.

Any TypeScript project would be an example to demonstrate DI, but I chose a Node/Express app for this article. I assume people who work with TypeScript either directly work with Node/Express servers, or have an idea about them.

When you inspect package.json , you can see a couple of dependencies. Let me go through them briefly:

express : Express is a popular framework for writing Node.js RESTful servers.

: Express is a popular framework for writing Node.js RESTful servers. reflect-metadata : A Polyfill library for Metadata Reflection API. This library allows other libraries to use metadata through decorators.

: A Polyfill library for Metadata Reflection API. This library allows other libraries to use metadata through decorators. ts-node: Node.js can’t run TypeScript files. Before you can run your code, you need to compile TypeScript into JavaScript. ts-node handles this process for you.

Node.js can’t run TypeScript files. Before you can run your code, you need to compile TypeScript into JavaScript. ts-node handles this process for you. typedi: TypeDI is a dependency injection library for TypeScript. We’ll see the use cases in a short time.

TypeDI is a dependency injection library for TypeScript. We’ll see the use cases in a short time. typescript: We are using TypeScript for this project, so we need to add it as a dependency.

We are using TypeScript for this project, so we need to add it as a dependency. @types/express: Type definitions for Express library.

Type definitions for Express library. @types/node : Type definitions for Node.js.

: Type definitions for Node.js. ts-node-dev: This library allows you to run TypeScript files and watch changes on certain files.

There are a few important compiler options you need to pay attention to. If you take a look at tsconfig.json, you can see the options for the compilation process:

We specify types for reflect-metadata and node .

and . We have to set emitDecoratorMetadata and experimentalDecorators flags to true.

All of our source code is inside the src folder. src/index.ts is our entry point. This file contains all the bootstrap steps of the server:

This code is a minimal Express server that has only one endpoint. When you send a GET request to /users route, it will return a list of users. The crucial part of the main function is the “Container.get” method. Please pay attention that we didn’t use the new keyword or instantiate an object. We just asked for the IoC container to return us a UserController instance. Then we bind the route and the controller method.

Our application is a dummy RESTful server, but I didn’t want it to be completely meaningless. I add four different folders to represent foundational sections of a complete backend. These are controllers, models, repositories, and services. Now let’s go through each of them:

Controllers folder contains our REST controllers . They are responsible for mediating communication between the user and the services. They receive requests and return responses.

folder contains our . They are responsible for mediating communication between the user and the services. They receive requests and return responses. Models folder contains our database entities . We don’t have a database connection, nor we need them but establishing a proper project structure has a major impact on learning. Let’s assume they are real database entities and continue on our journey.

folder contains our . We don’t have a database connection, nor we need them but establishing a proper project structure has a major impact on learning. Let’s assume they are real database entities and continue on our journey. Services folder contains our services . They are responsible for serving what REST controllers need by accessing different repositories.

folder contains our . They are responsible for serving what REST controllers need by accessing different repositories. Repositories folder contains our database access classes. We are using the Data Mapper pattern to perform database operations. In this pattern, we use repository classes to access the database and make operations.

We don’t do everything in a single class. There are many layers between request and response. This is called layered architecture. By doing a work-sharing between classes we make it easier to do dependency injection.

UserController has only one method. “getAllUsers” method is responsible for getting a result from user service and transmitting. We added a Service decorator to UserController class because we want this class to be managed by the IoC container. Inside the constructor method, we can see that this class needs a UserService instance. Again, we don’t have to control this dependency because the TypeDI container will create an instance for UserService, and when it generates the UserController instance, it will inject UserService.

UserService is very similar to UserController. We add a Service decorator to the class and we specify the dependencies we want inside the constructor method.

UserRepository is our final stop. We annotate this class with Service, but we don’t have any dependencies. Because we don’t have a database connection, I just added a hardcoded user list to class as private property.