Search code examples
node.jsexpressnconf

Attaching object to Node.js process


I am using the environment variable and arguments parsing module called nconf for my Node.js Express web server.

https://github.com/indexzero/nconf

I decided that the best way to make the nconf data global was to simply attach it to the process variable (as in process.env), is this a good idea or bad idea? Will it slow down execution in weighing down "process"?

Here is my code:

var nconf = require('nconf');

nconf.argv()
    .env()
    .file({ file: './config/config.json' });

nconf.defaults({
    'http': {
        'port': 3000
    }
});

process.nconf = nconf;

//now I can retrieve config settings anywhere like so process.nconf.get('key');

Frankly, I kind of like this solution. Now I can retrieve the config data anywhere, without having to require a module. But there may be downsides to this... and it could quite possibly be a very bad idea. I don't know.


Solution

  • It won't slow down the execution, but feels "smelly". It's hard to discover, and it will be difficult to test, if you ever decide you need to.

    A better solution would be to attach settings to a module and use require() to import it wherever needed.

    The best solution would be to just pass your settings object to the classes or modules that need it. Either directly, or as part of some kind of "global context".

    Eg.

    var global = {
      settings: {
        port: 8080
      }
    }
    
    //...
    
    global.api = new Api(global);
    
    //...
    
    function Api(global) {
      var port = global.settings.port;
    }
    

    UPDATE: more info on why the original pattern is bad:

    1) Discoverability

    You attach your settings to process.settings and go off to a different project. A year later, someone else takes over or you need to update things. Will you remember you attached your settings to process.nconf? Or was it process.settings?

    Now imagine you have 10 different global things, attached under different names, on different places.

    It's not as bad as attaching directly to the global context, but it's certainly better to clearly see where the stuff you're using is coming from (constructor or module).

    2) Testing

    You decide you need to test your module. So now you need to tweak your settings for each test instead of loading them from a file or argv. How do you do that?

    In case of the global process.nconf or require("settings") patterns, you need to do something like this:

    function canOpenAPIOnTheConfiguredPort(done) {
        var nconfSaveApiPort = process.nconf.api.port;
        process.nconf.api.port = '1234';
        var api = new Api();
        test.assertEqual(api.port, '1234');
        process.nconf.api.port = nconfSaveApiPort;
        done();
    }
    

    As your application grows, this quickly becomes annoying (eg. imagine having to mock 10 things). In comparison, here's how you do it using the dependency injection (constructor) pattern.

    function canOpenAPIOnTheConfiguredPort(done) {
        var api = new Api({
            port: '1234'
        });
        test.assertEqual(api.port, '1234');
        done();
    }