Node.js Application Configuration Files
December 13, 2010 9 Comments
What is the best practice to make configuration file for your Node.js application? Writing property file parser or passing parameters at command line is cumbersome.
Eval
One easy way to separate configuration and application code is by using eval
statement. Define your configuration as simple Javascript associative array and load and evaluage it on app startup.
Example configuration file myconfig.js
settings = { a: 10, // this is used for something SOME_FILE: "/tmp/something" }
Then at start of your application
var fs = require('fs'); eval(fs.readFileSync('myconfig.js', encoding="ascii"));
Now settings
object can be used as your program settings. e.g.
var mydata = fs.readFileSync(settings.SOME_FILE); for( i = 0 ; i < settings.a ; i++) { // do something }
Require
Another alternative to load configuration, as stated in comments, is to define configuration as module file and require
it.
//-- configuration.js module.exports = { a: 10, SOME_FILE: '/tmp/foo' }
In application code then require file
var settings = require('./configuration');
This prevents other global variable creeping in global scope, but it’s hackier to do dynamic configuration reloading. If you detect that file has changed, and would want to reload it at runtime you must delete entry from require cache and re-require the file. Another minor complication is that require uses its own search path (that you can override with NODE_PATH env. variable) so it’s more work to define dynamic location for configuration file in your app. (e.g. set it from command line).
// to reload file with require var path = require('path'); var filename = path.resolve('./configuration.js'); delete require.cache[filename]; var tools = require('./configuration');
Plain javascript as configure file has benefit (and downside) that it’s possible to run any javascript in the settings. For example.
settings = { started: new Date(), nonce: ~~(1E6 * Math.random()), a: 10, SOME_FILE: "/tmp/something" }
Both of these methods are mostly matter of taste. Eval is bit riskier as it allows leaking variables to global namespace but you’ll never have anything “stupid” in the configuration files anyway. Right?
JSON file
I’m not fan of using JSON as configuration format as it’s cumbersome to write and most editors are not able to show syntax errors in it. JSON also does not support comments that can be a big problem in more complicated configuration files.
Example configuration file myconfig.json
{ "a":10, "SOME_FILE":"/tmp/something" }
Then at start of your application read json file and parse it to object.
var fs = require('fs'); var settings = JSON.parse(fs.readFileSync('myconfig.json', encoding="ascii"));
And then use settings as usual
var mydata = fs.readFileSync(settings.SOME_FILE); for( i = 0 ; i < settings.a ; i++) { // do something }
Merging configuration files
One way to simplify significantly configuration management is to do hierarchical configuration. For example have single base configuration file and then define overrides for developer, testing and production use.
For this we need merge function.
// merges o2 properties to o1 var merge = exports.merge = function(o1, o2) { for (var prop in o2) { var val = o2[prop]; if (o1.hasOwnProperty(prop)) { if (typeof val == 'object') { if (val && val.constructor != Array) { // not array val = merge(o1[prop], val); } } } o1[prop] = val; // copy and override } return o1; }
You can use merge to combine configurations. For example, lets have these two configuration objects.
// base configuration from baseconf.js var baseconfig = { a: "someval", env: { name "base", code: 1 } } // test config from localconf.js var localconfig = { env: { name "test" db: 'localhost' }, test: true }
Now it’s possible to merge these easily
var settings = merge( baseconfig, localconfig ); console.log( settings. a ); // prints 'someval' console.log( settings.env.name ); // prints 'test' console.log( settings.env.code ); // prints '1' console.log( settings.env.db ); // prints 'localhost' console.log( settings.env.test ); // prints 'true'
Thanks, this really helped me!
Pingback: CodeBudo » Blog Archive » Application Configuration for Node.js
There is a small typo in your post, the code to read a file and eval should be:
eval(fs.readFileSync(‘myconfig.js’, encoding=”ascii”));
Thanks! Fixed.
Why not just store your settings as a module? Then you can utilize require() to keep a single reference to it.
appSettings.js:
exports.settings =
{ settingName: ‘this is cool!’
, settingName2: true
}
Usage:
var settings = require(‘./appSettings.js’).settings;
console.log(settings.settingName);
require() is good way to load settings, however I wanted to keep settings strictly as separate from the modules. Matter of taste methinks.
Why on earth would you use eval? eval is evil. Use JSON instead, or with comments it’s cjson: https://github.com/kof/node-cjson
With a dead simple .properties file: https://github.com/Gagle/Node-Properties
Nice article, thanks for sharing… one small addition… for your json file, you don’t have to use filesystem to load it.
var settings = require(‘myconfig.json’);
loads the json file as a json object in the settings variable (pretty much the same as your code did, but in one line).