I am looking for the best way to employ global configuration settings in my Node applications. In order of (my) preference the ways i have found are:

  1. Attach config to global object

    global.config = {
        db: require('./config/db'),
        server: require('./config/server'),
        session: require('./config/session'),
        auth: require('./config/auth')
    };
    
  2. Pass config object to modules that need it.

    var config = {
        db: require('./config/db'),
        server: require('./config/server'),
        session: require('./config/session'),
        auth: require('./config/auth')
    };
    var responder = require('./responder')(config);
    
  3. Require config files in each module. Since I usually split my config into seperate files I really do not like doing this. Since I dont always use certain files this also usually involces checking if files exist.

Is there any reason why one should avoid either of these methods? Is there any reason why one should be preferred over the others?

有帮助吗?

解决方案 2

For small project all three ways are acceptable. For big I can say next:

Global variable is a problem

If you start to use this way, you need to defend config object like var config = {...}; config.freeze();. In any cases global variables is a bad practice, for NodeJS especially, because it destructs modular system.

Passing config is the best way

That is the reason? TESTING

In tests you need to get some states of your config file. The first and third ways provides you next code style:

config.js

module.exports= {
  a: 10
};

app.js

var config = require('config');

module.exports.func = function(){
  if (config.a > 10) return 'A';
  return 'B';
}

Mocha+Chai test

var expect = require('chai').except,
    config = require('config'),
    app = require('app');

describe('Func', function(){
  it('return "A" if a > 10', function(){
    config.a = 12; //DHOOO!!! (c) Homer Simpson
    expect(app.func()).to.equal('A');
  });
  it('return "B" if a <= 10', function(){
    config.a = 9;
    expect(app.func()).to.equal('B');
  });
  config.a = 12; //return default state of config. DHOOO!!!
});

How you can see you need to have editable config, that is a bad practice (big project where each developer can change state of config in any place... DHOOO!!!)

And for second way it looks like this:

config.js

var config = {
  a: 10
};
config.freezy();
module.exports = config;

app.js

module.exports.func = function(config){
  if (config.a > 10) return 'A';
  return 'B';
}

Mocha+Chai test

var expect = require('chai').except,
    app = require('app');

describe('Func', function(){
  it('return "A" if a > 10', function(){
    expect(app.func({a:12})).to.equal('A');
  });
  it('return "B" if a <= 10', function(){
    expect(app.func({a:9})).to.equal('B');
  });
});

UPDATE

In this example func is very syntetic, for real project you can see something like this:

module.js

var SubModule = require('submodule');

function MyModule(config, someVar) {
 //Don't use full config, only options you needed.
 //Pull out config options
  this._a = config.a;
  this._b = config.b;

  this.doSomethink(someVar);
  this.subModule = new SubModule(config);
}

MyModule.prototype.doSomething = function(){
  if (this._a > 10) return 'A';
  return 'B';
}

module.exports = MyModule;`

submodule.js

function MySubModule(config) {
  this._c = config.c;
}
module.exports = MySubModule;

其他提示

In my experience it is common use and good style to go with the option No. 2: Pass config options to modules that need it you suggested.

Reasons:

  • It decouples configuration from actual logic. If you include configuration files within the module itself, there's a needless dependency to one specific configuration file.
  • There's still a defined dependency on specific configuration values which are provided as parameter - and not "magically" pulled from a global namespace which makes code hard to read, maintain and test.

This is by the way a rule of thumb for almost every language that allows things like global variables/objects and constructs for including "everything you like everywhere you like". But requirejs already pushes you a bit into the right direction by at least allowing exports to be a function that immediately accepts configuration. So the one-liner is an elegant way for requiring and configuring resources.

Everything beyond that would probably end up in a discussion about dependency injection (DI) concepts - which is a separate topic.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top