Tag Archives: configuration

Using npm config

Intro

I think most people (I don’t interact with most people so I don’t know really) don’t know about the various wonders npm does (or tries to do). One of them is that you can totally use npm as a build tool, an alternative to Grunt or Gulp.

In this post I’m going to discuss how you can use npm as a configuration system on top of your module written in (probably) node.js.

But first, let me discuss how people have been addressing the problem of configuration.

Popular config systems

Environment variables

Of course the most popular option would be to use environment variables. There are different ways to set environment variables in various operating systems but once set, these are passed onto your program whenever it is run. In node.js you can access an environment variable MY_AWESOME_VAR using process.env["MY_VAR"]. Environment variables can be considered per-user and thus has a consequence: conflicts can occur between programs. Some programs address this by adding a prefix. Many don’t.

.env files

Since setting environment variables on an operating system can be tedious, and have a huge scope with many possible conflicts (per-user scope), some have come up with .env files. Dot env files are files that you usually put on the root of your module directory, and when you run your module, it will import the .env file to your environment variable store. That way, you can both read from your OS’ environment variables and possibly override them in the .env file to provide a per-directory configuration. Packages enabling this functionality include dotenv.

Runtime command-line arguments

Of course you can always enable configuration setting via command-line arguments passed to your program. Just like mongo -u user -p=pass. This provides a very specific scope, specific to your program’s running instance. But if you’re typing this manually, it could be tedious. I mean, who here are still typing their usernames and passwords whenever they git push? Anyone?

From a database

What? Really? That’s a thing? I didn’t know that…

The awesome: npm config

The basics

What most people don’t know is that they could do all of these without using any dependencies, using just npm. (Except for the database thing. That’s so stupid… Why do that?)

Okay, let me clear this out of the way first. You’re probably reading this because you didn’t understand how npm config works, but here are all of the relevant documentation:

  • npm-config – Tells you what typing npm config should do, but not exactly why or how?
  • misc/config – Has some info but the bulk is npm’s own config
  • npmrc – Fair enough.

What I’m gonna teach here is how to create a configuration system that will be specific to your own module, and nothing else.

Supposing we’re done, let’s show first how you would read a configured variable such as password in a node.js app:

process.env.npm_package_config_password

Well, that’s easy enough. But wait. Aren’t you curious to know what all of that does? I know I am.

  • npm is just a prefix to all of the environment variables npm inserts. You know, to avoid conflicts.
  • package is a prefix to package-specific variables. If you have a package.json file (you should) a bunch of things from that will appear in your process.env prefixed with npm_package.
  • config is, specifically, your package-specific config. Which I am discussing right now.
  • password is the name of the config var.
  • _ (underscore) is the separator npm uses.

Suppose you are a module author authoring the module awesome-point-of-sale that requires a connection to a MySQL database. From the above you would know what to pass to create a connection:

const MySQL = require('mysql')
let connection = MySQL.createConnection({
  host: process.env.npm_config_package_host,
  user: process.env.npm_config_package_user,
  password: process.env.npm_config_package_pass,
  database: process.env.npm_config_package_db
})

Now, what you want to do is to ask your users to configure their system so they would have the correct configuration. One thing they can do is to set a per-user config:

npm config set awesome-point-of-sale:host 192.168.0.123

This will save the said config inside a .npmrc file in your $HOME directory. On Windows, this is the %USERPROFILE% directory. Don’t be shocked why they do this right there, it’s standard practice, really.

Alternatively, they can open a text editor using:

npm config edit

They would then append the following line in .env format:

awesome-point-of-sale:host=192.168.0.123

There are other places where you could put .npmrc config files. See the npmrc docs for details. With this it is possible to set config vars in different scopes: per-system, per-user, and per-directory. Hint: Try npm config edit -g!

Now, how would npm insert all this stuff in your program?

You should totally run your program using npm run-script. Google “npm as build tool” for ideas. (Although I’m considering blogging it myself. I have some personal opinions.)

And that’s it! We’re done. Wait, not yet? Yes, there’s more to it than it seems.

Defaults

Yes, definitely! You can set your project defaults in your config. Suppose you are able to run a MySQL database locally. We can use that.

package.json:

{
  "config": {
    "host": "localhost",
    "user": "admin",
    "pass": "",
    "db": "pos"
  }
  ...
}

Grouped configs

What if you want to group your config vars such that there’s a separate config for how to connect to your MySQL database and how to start your server? What you can do is group your configs into objects:

package.json:

{
  "config": {
    "mysql": {
      "host": "localhost",
      "user": "admin",
      ...
    },
    "server": {
      "host": "0.0.0.0",
      "port": 80
    }
  }
  ...
}

Now how would this reflect in your program? What npm does is it uses the underscore character (_) to indicate the path to the variable:

let serverHost = process.env.npm_package_config_server_host
let serverPort = process.env.npm_package_config_server_port
let mysqlHost = process.env.npm_package_config_mysql_host

How will users set this in the config though? The same way:

npm config set awesome-point-of-sale:server_host 0.0.0.0

And that’s a wrap!

Whoo! Aren’t you glad that’s over? Although if you have an absurd module name such as awesome-point-of-sale you might want to consider adding some config functionality. Something like npm init, even. (Whoa, that’s a really good suggestion right there that I could use myself.)