Dependency Injection with Node.js

  • Post published:May 31, 2020

Node.js supports the Dependency Injection (DI) design pattern by utilizing the awilix npm package. Awilix is a powerful dependency injection container for JavaScript/Node providing a very simple API.

Another useful package is awilix-express. This package provides helpers for awilix as well as router and scope-instantiating middleware for express.

Before proceeding with this article, ensure that you have node.js and visual studio code installed.

Getting started

Create a new project that handles e6+ features by using babel, and the visual studio code debugger. Read this article to set up the project. After setting up the project, install the below babel plugins to handle the decorator pattern and the async/await es6 feature.

npm install --save-dev @babel/plugin-proposal-decorators
npm install --save-dev @babel/plugin-transform-runtime

Modify the babel.config.json file to include the new babel plugins.

Install the below dependencies for the project.

npm install awilix
npm install awilix-express
npm install body-parser
npm install compression
npm install cors
npm install express

The above setup is the basic configuration needed to get started with creating a RESTful API with node.js.

Creating app config

In the src folder, create a new folder called app. In the app folder, create a new folder called configs and add the following app.config.js file.

Creating a heroes route

In the app folder, create a new folder called routes. In the routes folder, create a new folder called heroes with the below files.

From the above code base, it can be seen that the decorator pattern was used to create the controller. Also, a mock repository was created to get a list of heroes.

Creating the app

In the app folder, create a file called app.js with the below code.

The above class is standard for most express setups, with the exception of an additional two important middleware setups:

On line 31,

app.use(scopePerRequest(container));

the scopePerRequest function creates a scope for each request.

On line 32,

app.use(loadControllers('routes/**/*.controller.js', {cwd: __dirname}));

the loadControllers function loads all controllers in the routes folder relative to the current working directory. This is a global pattern, thus new routes will automatically get registered.

Creating the DI container

In the app folder, create a new file called bootstrap.js with the below code.

The following is observed from the above code base.

On line 23,

const container = createContainer({injectionMode: InjectionMode.CLASSIC});

a new container is created using injectionMode.CLASSIC. This mode indicates that the container is set to use constructor injection by parsing the function/constructor parameters, and matching them with registrations in the container. Note that awilix advises not to use this if the code base is going to be minified.

From lines 25 to 30,

container.register({
    app: asClass(App).singleton(),
    appConfig: asClass(AppConfig).singleton(),
    heroesRepository: asClass(HeroesRepository).singleton(),
    heroesService: asClass(HeroesService).singleton()
});

the container is been set up to indicate how a service is to be resolved, for example, whenever a heoresRepository is requested, it is instantiated by a class called HeroesRepository.

Awilix provides the following lifetime types.

  • Transient: This is the default. The registration is resolved every time it is needed. This means that if a class is resolved more than once, then a new instance will be created every time.
  • Scoped: The registration is scoped to the container meaning that the resolved value will be reused when resolved from the same scope.
  • Singleton: The registration is always reused meaning that the resolved value is cached in the root container.

Creating the server.js file

In the src folder, create a new file called server.js with the below code. This file will be called whenever the application is started.

Run the application

Run the application by pressing F5. Open postman and create a get request to retrieve the mock heroes.

theCodeReaper

Summary

Awilix and awilix-express have made it very easy to set up a dependency injection container in a node.js project. This article was only an introduction to dependency injection with node.js. Awilix provides a lot more information to customizing a container based on the requirements of a project.

The source code used in this article can be found here.

Further information on dependency injection in node.js can be found at the following links.

Software versions used in this article

  • node v12.17.0
  • npm v6.14.5
  • visual studio code v1.45.1
  • babel v7.10.1
  • awilix v4.2.6
  • awilix-express v3.0.0
  • postman v7.25.0