A Node.js Configuration Management Alternative

I’ve been working in node.js for some time now. In my time thus far it seems to me that the node.js community highly values explicitness. That is, it’s better to be up-front with your intentions than to always be looking for tricky and clever ways to implement solutions. With this in mind, you will, for example, often see explicit require statements at the beginning of files when working in node. I like this, despite these series of requires being blocking, syncrhonous calls. However, for the sake of making things interesting, I want to present an alternative to managing these requires. In addition, I will present this alternative in context of managing configs and directory structure in a node.js application.

The following directory structure is something I personally use. The Modules object, discussed later on, is not something I use, but something I thought would be interesting to discuss.

The example in this blog post will cover similar conventions to other web frameworks. Let’s first take a look at the directory structure and important files.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/
  app/
    models/
    resources/
  config/
    application.js
    routes.js
  lib/
  test/
  node_modules/
  playbooks/
    config/
      group_vars/
      roles/
    deploy/
      group_vars/
      roles/
  config.js
  modules.js
  server.js
...
  1. app is where you store you application code. This application in particular has the sole purpose of being an API. The resources directory is exactly for what is sounds like. Moreover, this directory caters to percolator, a framework for building APIs.
  2. config is where you store you application configs.
  3. lib is for library files that may be used across services or for handling legacy or miscellaneous components.
  4. test is for mocha tests.
  5. node_modules is not committed to source control. It’s looked at first in determining binaries and packages to use when running your node app.
  6. playbooks is for Ansible, a tool that allows for simple server orchestration and deployment. This app uses ansible to orchestrate different server types (api, web, mail, cache, queue) across multiple data centers using YAML files.

So now that we have the directory structure layed out, what are those files in there? That is, how do we start the node app and what happens during the boot process? Let’s take a look at the server.js file:

1
2
3
4
5
6
process.title = 'foo-app'

var App = require('./config/application');
var Foo = new App(__dirname);

Foo.start();

The server.js file loads the application.js config file:

1
2
3
4
5
6
Modules = require('../modules');
var Config = require('../config');

function Application (rootDirectory) {
  ... do stuff to setup and start the app ...
}

Here in this example you’ll notice a Modules object. This object is an interesting point in this configuration management pattern. The object is accessible throughout the application and provides a central way of managing node packages in use:

1
2
3
4
5
6
7
8
9
10
11
12
13
module.exports = {
  _:            require('underscore'),
  os:           require('os'),
  percolator:   require('Percolator').Percolator,
  mysql:        require('mysql'),
  sequelize:    require('sequelize'),
  redis:        require('redis'),
  kue:          require('kue'),
  cluster:      require('cluster'),
  memcached:    require('memcached'),
  logger:       require('log-driver').logger,
  moment:       require('moment'),
};

The good thing about the above pattern is centralizing node packages in use into one area such that a person new to the codebase would be able to easily see what the application relies on. However, the question is: does this matter? Although this is useful, I think the question is not as important as asking “What does component X do?”. In the standard way, using require statements in indivudal files on a as-needed basis, the file would explicily use the required files and nothing else. I personally like this better for precisely this reason. The above pattern falls in comparison here since every package would be accessible in every file, which might not be necessary and convoluted. However, the above pattern does process all requires at once on application start, as opposed to processing the requires at runtime. Basic benchmarking shows this to be a micro-optimization.

In summary, this post entertains an alternative configuration management pattern for node applications where I implement patterns similarly to those found in other web frameworks. In comparing to standard node.js configuration styles, the performance benchmarks between the two are insignicant, thus leading to a fairly inconclusive statement: It’s all about preference.