Do I need dependency injection in NodeJS, or how to deal with ...?


Question

I currently creating some experimental projects with nodejs. I have programmed a lot Java EE web applications with Spring and appreciated the ease of dependency injection there.

Now I am curious: How do I do dependency injection with node? Or: Do I even need it? Is there a replacing concept, because the programming style is different?

I am talking about simple things, like sharing a database connection object, so far, but I have not found a solution that satisfies me.

1
197
5/7/2013 8:08:34 PM

Accepted Answer

In short, you don't need a dependency injection container or service locater like you would in C#/Java. Since Node.js, leverages the module pattern, it's not necessary to perform constructor or property injection. Although you still can.

The great thing about JS is that you can modify just about anything to achieve what you want. This comes in handy when it comes to testing.

Behold my very lame contrived example.

MyClass.js:

var fs = require('fs');

MyClass.prototype.errorFileExists = function(dir) {
    var dirsOrFiles = fs.readdirSync(dir);
    for (var d in dirsOrFiles) {
        if (d === 'error.txt') return true;
    }
    return false;
};

MyClass.test.js:

describe('MyClass', function(){
    it('should return an error if error.txt is found in the directory', function(done){
        var mc = new MyClass();
        assert(mc.errorFileExists('/tmp/mydir')); //true
    });
});

Notice how MyClass depends upon the fs module? As @ShatyemShekhar mentioned, you can indeed do constructor or property injection as in other languages. But it's not necessary in Javascript.

In this case, you can do two things.

You can stub the fs.readdirSync method or you can return an entirely different module when you call require.

Method 1:

var oldmethod = fs.readdirSync;
fs.readdirSync = function(dir) { 
    return ['somefile.txt', 'error.txt', 'anotherfile.txt']; 
};

*** PERFORM TEST ***
*** RESTORE METHOD AFTER TEST ****
fs.readddirSync = oldmethod;

Method 2:

var oldrequire = require
require = function(module) {
    if (module === 'fs') {
        return {
            readdirSync: function(dir) { 
                return ['somefile.txt', 'error.txt', 'anotherfile.txt']; 
            };
        };
    } else
        return oldrequire(module);

}

The key is to leverage the power of Node.js and Javascript. Note, I'm a CoffeeScript guy, so my JS syntax might be incorrect somewhere. Also, I'm not saying that this is the best way, but it is a way. Javascript gurus might be able to chime in with other solutions.

Update:

This should address your specific question regarding database connections. I'd create a separate module for your to encapsulate your database connection logic. Something like this:

MyDbConnection.js: (be sure to choose a better name)

var db = require('whichever_db_vendor_i_use');

module.exports.fetchConnection() = function() {
    //logic to test connection

    //do I want to connection pool?

    //do I need only one connection throughout the lifecyle of my application?

    return db.createConnection(port, host, databasename); //<--- values typically from a config file    
}

Then, any module that needs a database connection would then just include your MyDbConnection module.

SuperCoolWebApp.js:

var dbCon = require('./lib/mydbconnection'); //wherever the file is stored

//now do something with the connection
var connection = dbCon.fetchConnection(); //mydbconnection.js is responsible for pooling, reusing, whatever your app use case is

//come TEST time of SuperCoolWebApp, you can set the require or return whatever you want, or, like I said, use an actual connection to a TEST database. 

Do not follow this example verbatim. It's a lame example at trying to communicate that you leverage the module pattern to manage your dependencies. Hopefully this helps a bit more.

101
2/13/2012 6:43:47 PM

require is the way of managing dependencies in Node.js and surely it is intuitive and effective, but it has also its limitations.

My advice is to take a look at some of the Dependency Injection containers available today for Node.js to have an idea on what are their pros/cons. Some of them are:

Just to name a few.

Now the real question is, what can you achieve with a Node.js DI container, compared to a simple require?

Pros:

  • better testability: modules accepts their dependencies as input
  • Inversion of Control: decide how to wire your modules without touching the main code of your application.
  • a customizable algorithm for resolving modules: dependencies have "virtual" identifiers, usually they are not bound to a path on the filesystem.
  • Better extensibility: enabled by IoC and "virtual" identifiers.
  • Other fancy stuff possible:
    • Async initialization
    • Module lifecycle management
    • Extensibility of the DI container itself
    • Can easily implement higher level abstractions (e.g. AOP)

Cons:

  • Different from the Node.js "experience": not using require definitely feels like you are deviating from the Node way of thinking.
  • The relationship between a dependency and its implementation is not always explicit. A dependency may be resolved at runtime and influenced by various parameters. The code becomes more difficult to understand and debug
  • Slower startup time
  • Maturity (at the moment): none of the current solutions is really popular at the moment, so not so many tutorials, no ecosystem, not battle tested.
  • Some DI containers will not play well with module bundlers like Browserify and Webpack.

As with anything related to software development, choosing between DI or require depends on your requirements, your system complexity, and your programming style.


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon