Inversion of Control in NodeJS

Why and how we built our own IoC library

Sharing is caring!

by Julian Alessandro


Struggling to unlock the full potential of your technology? We’ve got you covered! Reach out now to discuss how we can propel your business to new heights together!

A Glimpse into Inversion of Control (IoC)  

Did you know that node.js powers more than 22 million apps in the world? At NaNLABS, we've seamlessly added node.js into our toolkit, leveraging its speed, scalability, and efficiency.

Now let’s talk more about the Inversion of Control in node.js.

For someone with a Java and Spring background, IoC pattern is a great way to organize your application’s code.

So, what’s that pattern about? In software engineering, Inversion of Control (IoC) is a design principle involving transferring the control of objects or portions of a program to a container or framework. Compared to traditional programming, the framework calls into the custom or task-specific code and not the other way around. 

Uncover more about the Inversion of Control with this thorough explanation by Martin Fowler

The good thing about IoC is that it is not a framework itself, but actually a framework builder!

Ready to harness the full power of node.js? Let's explore how to do it efficiently and elevate your project to new heights!

Under the hood of Inversion of Control

The library works by parsing such DSL and adding the components defined to access them globally. This is done in three phases:

Looking for a tech partner to support your development process? Check out these 6 First-rate Node.js Development Companies! Your perfect match might be on the other side of that link ;)

Implementing Inversion of Control: Component Configuration

The main method we use is "module" (line 20), which creates the container where all the components will be located. This method, like the rest of the methods in this code, uses the Method Chaining pattern. In this case, it allows us to add the through the component method. The array of strings passed as parameter enumerates the dependencies of our container.

Once the container is created, we define a new component called (21) invoking the component method that receives two arguments:

  • A unique identifier.

  • The configuration object.

Let’s talk about the second argument.

The object must have a class property so we can know what to instantiate. Basically, with this property we are saying that we’ll add to the container a B object called .

Also, and here is one of the big potentials of our lib, this config object could have properties that will end up being attributes of our new component (23). With this, we can assign other components or values to ours.

In this example,  we’ll a property called ‘a’ referencing the component. The “ref” method will search in the container for a component called “a” that must exist. But, we didn’t define “a” anywhere. This works because we passed the dependency to the module method which contains the definition.

These values could be whatever we want, for example, a string, or an integer. We should be careful with this feature because the components are singletons, and it is a good practice to keep the attributes of a singleton object immutable.

The build method (26). Why do we pass the configuration to the method? Because we can share immutable data between all components. You probably noticed that we never talked about line 24, right? Until now. We pass a config object to the method because we want to share data between components like the user object. A concrete example could be a database configuration.

Here is when we use our DSL to specify the way that you could access the data. As in line 24, you define a property like the others () and you need to assign the path to the property of the config object that we passed to the build method using String interpolation.
This way, we are saying to nan-ioc to look in the configuration object for a property called with a nested property called .

The build method is the last thing that you need nan-ioc to do before starting to build the container. Here is when all the components are initialized and injected between them.
This method triggers the building phase when the tree is resolved and would fail if a circular dependency was found.

After each component is built, nan-ioc calls method. This method is not required and will be called only if you defined it. At this point, the component has all its dependencies built.

An example of the usage of this hook is when you need to have all the modules defined and built to start a server.

Overrun with development tasks? We can help! Get in touch to discuss how our dedicated Agile squad can blend with your team to reach your goals! 

Next steps: Refining the module

For us the job is never quite done, so we want to keep improving the module. These are some of the aspects that we want to work on:

  • Improve error handling: When a circular dependency is detected, an error is thrown. But currently we are not showing exactly which dependency is the wrong one.

  • Define a component using Annotations: To define a component (with its dependencies) we need to create a separate file that handles that. It would be great if we could define components, factories and dependencies directly in the code using Annotations.

  • Allow the use of multiple containers: To keep concerns properly separated, we would like to be able to define and use multiple containers. In theory we can, but we haven’t properly tested it.

  • Define component’s scope: At the building phase we register all the components under the same global scope. We want to add the ability to define different scopes where the components can live.

  • Asynchronous construction of components: For the time being, our library only supports that the components are built synchronously. However, we know Javascript is all about asynchronous code; this is a great addition to the library.

  • Directories auto-scan: To add any component, we have to manually define the folders where they are. A good enhancement would be scanning automatically each folder in the project.

You can start using the current version of the library from NPM. Visit our GitHub profile to get more resources, tips, and tools.

Want to unlock more node.js marvels? Dive into the HyreCar case study and discover how we helped them turn their monolith legacy architecture into a Cloud-native platform with node.js!

Frequently Asked Questions About Inversion of Control

  • What is meant by Inversion of Control (IoC)?

    Inversion of Control means delegating the control of the program’s flow to an external framework or container.

  • How does Inversion of Control (IoC) work?

    When implementing Inversion of Control, a framework is responsible for creating and managing the dependencies that the code needs, making the code more modular and flexible.

  • What’s the difference between Inversion of Control (IoC) and Dependency Injection (DI)?

    Inversion of Control (IoC) focuses on delegating control. As part of IoC, Dependency Injection focuses on how the dependencies are structured and managed within the app.

  • Why is Inversion of Control (IoC) useful?

    Implementing Inversion of Control makes the code more modular and flexible. It also enhances scalability and testability, making the codebase more organized and adaptable.

More articles to read

Previous blog post

Web Technologies


5 tips to write animations' specs that developers will love

Read the complete article

Next blog post



Stability during large refactors

Read the complete article