Dependency Injection Container for JavaScript (Node.js): Awilix

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Dependency Injection Container for JavaScript (Node.js): Awilix



In modern Node.js development, maintaining clean architecture and code testability is critically important. One of the key patterns that helps with this is Dependency Injection (DI). To effectively implement this pattern in the JavaScript environment, specialized libraries have been created — Dependency Injection containers. In this article, we will take a detailed look at one of the most popular and lightweight libraries — Awilix.



What is Dependency Injection and why do you need a container?



In the traditional approach, an object creates its own dependencies (for example, using the new operator). This leads to tight coupling of code, which complicates testing and modification. The Dependency Injection pattern proposes passing (injecting) dependencies from the outside. The container automates this process: it knows how to create objects and their dependencies, and provides ready-made instances on request.



Awilix: a lightweight and powerful DI container



Awilix is a library for JavaScript (Node.js) that implements the Inversion of Control (IoC) pattern using a container. It is written from scratch for Node.js, supports asynchronous dependency resolution, various registration styles (classes, factories, values), and automatic dependency discovery via the constructor.



Key features:

  • Automatic injection: The container analyzes constructor or function parameters and automatically injects the required dependencies.
  • Asynchronous support: Dependencies can be asynchronous (e.g., database connections), and the container correctly waits for their resolution.
  • Various registration modes: You can register classes (asClass), factory functions (asFunction), or ready-made values (asValue).
  • Scopes: Different object lifecycles are supported (singleton, transient, scoped).
  • Modularity: The container can be easily split into modules and dependencies can be registered in different parts of the application.


Installation and basic usage



Installing Awilix is done via npm:



npm install awilix


Let's look at a simple example. Suppose we have a service for working with users that depends on a logger and a repository.



const { createContainer, asClass, asFunction, asValue } = require('awilix');

// 1. Define dependenciesclass Logger { log(message) { console.log(`[LOG]: ${message}`); }}

class UserRepository { constructor({ logger }) { this.logger = logger; }

findById(id) { this.logger.log(`Searching for user with ID: ${id}`); return { id, name: 'John Doe' }; }}

class UserService { constructor({ userRepository, logger }) { this.userRepository = userRepository; this.logger = logger; }

getUser(id) { this.logger.log('Request to UserService'); return this.userRepository.findById(id); }}

// 2. Create a container and register dependenciesconst container = createContainer();

container.register({ logger: asClass(Logger).singleton(), userRepository: asClass(UserRepository).singleton(), userService: asClass(UserService).singleton()});

// 3. Use the container to get the serviceconst userService = container.resolve('userService');const user = userService.getUser(1);console.log(user);// Output:// [LOG]: Request to UserService// [LOG]: Searching for user with ID: 1// { id: 1, name: 'John Doe' }<

Recommendations