Skip to content

Config files how to

Alexander edited this page Jan 21, 2016 · 11 revisions

How to use require-dir-all to handle config files

Note: If you are interesting in solution, not in the problem :) you may jump directly to the solution.

Problem definition

If you are not using some config preprocessor like nconf or config, most probably you are using .js or .json files. You may prefer .js for flexibility (ability to comment and to transform the values) or .json for their strictness.

At the early beginning you may have only one config file, like this:

config.json:

{
  "protocol": "http",
  "hostname": "localhost",
  "port": 8080
}

app.js:

'use strict';

var express = require('express');
var http  = require('http');
var https = require('https');

var config = require('./config');

...

As your app evolves, your config is growing too:

config.json:

{
  "client": {
    "protocol": "http",
    "hostname": "localhost",
    "port": 8080,
  },
  "server": {
    "http": {
      "port": 8080
    },
    "https": {
      "port": 8081,
      "cert": "./cert.pem",
      "key": "./key.pem"
    }
  }
}

At some point your single file appears to be too hard to manage. When you see that, you split the file into several smaller files, each for own functional part of application and put them into separated directory. You may also group configs into separated subdirectories:

./config/client.json:

{
  "protocol": "http",
  "hostname": "localhost",
  "port": 8080,
}

./config/server/http.json

{
  "port": 8080
}

./config/server/https.json

{
  "port": 8081,
  "cert": "./cert.pem",
  "key": "./key.pem"
}

That means, you'll get several requires in your app files instead if just one require as it was before:

...

# var config = require('./config');
var clientConfig = require('./client');
var httpConfig   = require('./server/http');
var httpsConfig  = require('./server/https');

...

var httpServer = http.createServer(app);        
httpServer.listen(httpConfig.port);

...

While files and directories names are follow application object naming conventions, you still need to manually match directory structure and file names to variables inside application.

Solution

With require-dir-all you able to read all the .json and .js config files into single object with just one line:

var config = require('require-dir-all')('config', {recursive: true}); 

It will read config directory into following object, with directories and base filenames as its keys:

{
  "client": {
    "protocol": "http",
    "hostname": "localhost",
    "port": 8080
  },
  "server": {
    "http": {
      "port": 8080
    },
    "https": {
      "port": 8081,
        "cert": "./cert.pem",
        "key": "./key.pem"
    }
  }
}

By default, index files will be added as properties named 'index' of the object. If you add server/index.json with following content:

{
  "name: "Demo Server"
}

The resulting object will be as follows:

{
 ...
  "server": {
    "index": {
      "name": "Demo Server"
    },
    ...
  }
}

You may assign values of index files directly to parent object instead of storing it into index property by setting option {indexAsParent:true}:

var config = require('require-dir-all')('config', {recursive: true, indexAsParent:true}); 

After that your config will have following value:

{
 ...
  "server": {
    "name": "Demo Server",
    ...
  }
}

Demo

You can find working demo example in demo/config/.

Notes

Mixing .js and .json files

You may freely mix .js and .json files. The only limitation is that if they have same base names, the content of .json will be merged over .js.

$ cat index.js
// Export object
module.exports = {
  prop_js: 'string exported from merge_js_json/index.js',
  prop: 'string exported from merge_js_json/index.js'
};

$ cat index.json 
{
  "prop_json": "string exported from merge_js_json/index.json",
  "prop": "string exported from merge_js_json/index.json"
}

Result will be:

{
  "prop_js": "string exported from merge_js_json/index.js",
  "prop": "string exported from merge_js_json/index.json",
  "prop_json": "string exported from merge_js_json/index.json"
}

Files and directories with the same name

As of latest package version on the moment of writing of this article, if you have subdirectory and single file with the same base names (excluding the exctension), the file content will be merged into the content of subdirectory. Example:

Properties of objects corresponding to subdirectories