Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Get Rid of Node.js Config Grunt Work

DZone's Guide to

Get Rid of Node.js Config Grunt Work

Nobody likes grunt work. Not even Node.js. Here's how to eliminate Node.js config grunt work.

· Performance Zone
Free Resource

Evolve your approach to Application Performance Monitoring by adopting five best practices that are outlined and explored in this e-book, brought to you in partnership with BMC.

We recently began using the NPM module config in our Node.js projects. It's a really nice way to enable our clients to configure their software once we deliver it. One of the nicest features, IMHO, is its facility that enables you to define custom environment variables for any given config setting.

For example, if your configuration file config/default.json looks like

{
  "dirs": {
    "upload": "/tmp",
    "extracted": "/tmp"
  }
}

then you can create another file called config/custom-environment-variables.json like

{
  "dirs": {
    "upload": "MYAPP_DIRS_UPLOAD",
    "extracted": "MYAPP_DIRS_EXTRACTED"
  }
}

that allows an administrator to override config values in .json files with environment variables. For example, if he sets MYAPP_DIRS_UPLOAD to /var/uploads, then config will return /var/uploads instead of /tmp. Nice, right?

However, for large projects or for lazy developers like me, it's easy to forget to add an entry in config/custom-environment-variables.json whenever you add an entry to config/default.json (or any other config file).

You see, I really hate grunt work.  Since I espouse the invaluable trait of laziness, I created & published an NPM module called config-cev-generator that takes care of creating the config/custom-environment-variables.json file for me. BTW, "cev" stands for "custom environment variable", which you probably already noticed. Now, you can have this generated for you!  Just

$ npm install -g config-cev-generator

and you're ready to go.

For example, in a Node.js project that uses config, open a command line in the root of the project and try the command

$ cev

You'll see all of your config variables ready for use as environment variables printed to stdout.  To save it, just redirect the output to config/custom-environment-variables.json. For example, given the config file above, cev generates

$ cev
{
  "dirs": {
    "upload": "NODE_APP_DIRS_UPLOAD",
    "extracted": "NODE_APP_DIRS_EXTRACTED"
  }
}

The default prefix is NODE_APP, separator is _, and environment variables (being environment variables) are upper cased by default. But wait! There's more!  cev is nicely configurable:

$ cev --help
USAGE: node cev [OPTION1] [OPTION2]... arg1 arg2...
The following options are supported:
  -s, --separator      Separator. ("_" by default)
  -p, --prefix         Prefix. ("NODE_APP" by default)
  -c, --casing         Casing: "upper", "lower", or "unchanged". ("upper" by default)
  -f, --pretty         Format prettily with given number of spaces for indentation. ("2" by default)
  -e, --empties              If present, preserves sections that wouldn't have any environment variables.  Functions are always skipped.
  -v, --verbose              Be verbose.

Invoking

$ cev --prefix MYAPP --casing lower

yields

{
  "dirs": {
    "upload": "myapp_dirs_upload",
    "extracted": "myapp_dirs_extracted"
  }
}

For small projects, who cares, unless your really lazy, right?  But, for large projects, this can become truly annoying. Consider this config/default.json file:

{
  "dirs": {
    "upload": "",
    "extracted": "",
    "regex": "",
    "format": ""
  },
  "tz": "",
  "webserver": {
    "name": "DEFAULT",
    "port": 4879,
    "pfx": "cert/cert.pfx",
    "passphrase": "",
    "public": "cert/public.crt.pem",
    "ca": "cert/root-ca.crt.pem",
    "cakey": "cert/root-ca.key.pem"
  },
  "encryption": {
    "public": {
      "keyfile": "keys/public.pem",
      "key": ""
    }
  },
  "email": {
    "username": "",
    "password": "",
    "from": "",
    "host": "smtp.google.com",
    "port": 25
  },
  "ios": {
    "root": "ios",
    "plist": "MyApp.plist.ejs",
    "ipa": "MyApp.ipa",
    "bundleIdentifier": "0000000000.com.scispike.MyApp",
    "subtitle": "SciSpike MyApp",
    "title": "SciSpikeMyApp"
  }
}

Writing the config/custom-environment-variables.json for that would just, well, suck. Check this out:

$ cev --prefix MYAPP
{
  "dirs": {
    "upload": "MYAPP_DIRS_UPLOAD",
    "extracted": "MYAPP_DIRS_EXTRACTED",
    "regex": "MYAPP_DIRS_REGEX",
    "format": "MYAPP_DIRS_FORMAT"
  },
  "tz": "MYAPP_TZ",
  "webserver": {
    "name": "MYAPP_WEBSERVER_NAME",
    "port": "MYAPP_WEBSERVER_PORT",
    "pfx": "MYAPP_WEBSERVER_PFX",
    "passphrase": "MYAPP_WEBSERVER_PASSPHRASE",
    "public": "MYAPP_WEBSERVER_PUBLIC",
    "ca": "MYAPP_WEBSERVER_CA",
    "cakey": "MYAPP_WEBSERVER_CAKEY"
  },
  "encryption": {
    "public": {
      "keyfile": "MYAPP_ENCRYPTION_PUBLIC_KEYFILE",
      "key": "MYAPP_ENCRYPTION_PUBLIC_KEY"
    }
  },
  "email": {
    "username": "MYAPP_EMAIL_USERNAME",
    "password": "MYAPP_EMAIL_PASSWORD",
    "from": "MYAPP_EMAIL_FROM",
    "host": "MYAPP_EMAIL_HOST",
    "port": "MYAPP_EMAIL_PORT"
  },
  "ios": {
    "root": "MYAPP_IOS_ROOT",
    "plist": "MYAPP_IOS_PLIST",
    "ipa": "MYAPP_IOS_IPA",
    "bundleIdentifier": "MYAPP_IOS_BUNDLEIDENTIFIER",
    "subtitle": "MYAPP_IOS_SUBTITLE",
    "title": "MYAPP_IOS_TITLE"
  }
}

Nice, huh?

Here's something that might get you bonus points amongst your fellow geeks (read:  lazy programmers). Add config-cev-generator to your package.json's devDependencies, then invoke the project-local cev command before anything interesting. For example, consider this package.json fragment:

  ...
  "devDependencies": {
    "config-cev-generator": "^0.1.4"
  },
  "scripts": {
    "cev": "$(npm bin)/cev --prefix COOLNESS > config/custom-environment-variables.json",
    "pretest": "npm run cev",
    "prestart": "npm run cev",
    "prerestart": "npm run cev",
    ...
  },
  ...

Now, whenever you run a test or your app, you're sure not to forget to keep your config/custom-environment-variables.json in sync with the rest of your config! Check this out:

$ npm install
npm WARN package.json cev-test@1.0.0 No description
npm WARN package.json cev-test@1.0.0 No repository field.
npm WARN package.json cev-test@1.0.0 No README data
config-cev-generator@0.1.4 node_modules/config-cev-generator
├── stdio@0.2.7
└── config@1.15.0
$ npm test

> cev-test@1.0.0 pretest /Users/matthew/temp/cev-test
> npm run cev


> cev-test@1.0.0 cev /Users/matthew/temp/cev-test
> $(npm bin)/cev --prefix COOLNESS > config/custom-environment-variables.json


> cev-test@1.0.0 test /Users/matthew/temp/cev-test
> echo 'you should run tests here'

you should run tests here
$ cat config/custom-environment-variables.json
{
  "dirs": {
    "upload": "COOLNESS_DIRS_UPLOAD",
    "extracted": "COOLNESS_DIRS_EXTRACTED",
    "regex": "COOLNESS_DIRS_REGEX",
    "format": "COOLNESS_DIRS_FORMAT"
  },
  "tz": "COOLNESS_TZ",
  "webserver": {
    "name": "COOLNESS_WEBSERVER_NAME",
    "port": "COOLNESS_WEBSERVER_PORT",
    "pfx": "COOLNESS_WEBSERVER_PFX",
    "passphrase": "COOLNESS_WEBSERVER_PASSPHRASE",
    "public": "COOLNESS_WEBSERVER_PUBLIC",
    "ca": "COOLNESS_WEBSERVER_CA",
    "cakey": "COOLNESS_WEBSERVER_CAKEY"
  },
  "encryption": {
    "public": {
      "keyfile": "COOLNESS_ENCRYPTION_PUBLIC_KEYFILE",
      "key": "COOLNESS_ENCRYPTION_PUBLIC_KEY"
    }
  },
  "email": {
    "username": "COOLNESS_EMAIL_USERNAME",
    "password": "COOLNESS_EMAIL_PASSWORD",
    "from": "COOLNESS_EMAIL_FROM",
    "host": "COOLNESS_EMAIL_HOST",
    "port": "COOLNESS_EMAIL_PORT"
  },
  "ios": {
    "root": "COOLNESS_IOS_ROOT",
    "plist": "COOLNESS_IOS_PLIST",
    "ipa": "COOLNESS_IOS_IPA",
    "bundleIdentifier": "COOLNESS_IOS_BUNDLEIDENTIFIER",
    "subtitle": "COOLNESS_IOS_SUBTITLE",
    "title": "COOLNESS_IOS_TITLE"
  }
}

You're welcome. And long live laziness.

The npm module is at https://www.npmjs.com/package/config-cev-generator and the public git repo is at https://github.com/SciSpike/node-config-cev-generator. Issues welcome; pull requests are more welcome!  :)

---
Matthew Adams
For more information, please contact info@scispike.com

Lightning Fast Software Development!

Learn tips and best practices for optimizing your capacity management strategy with the Market Guide for Capacity Management, brought to you in partnership with BMC.

Topics:
performance ,node.js ,node js

Published at DZone with permission of Matthew Adams, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}