@nautilus/config
v1.3.1
Published
Convention for your configuration
Downloads
2,133
Readme
@nautilus/config
Environment-aware loader designed to bring convention to your configuration.
Storing configuration separate from the code is one of the Twelve-Factor App principles. This module makes it easier for you to observe best practices around configuration while keeping organization as your project grows.
While sensitive environment variables should always be stored as process.env
values in Node, there are many other things which qualify as configuration that either don't change between environments, or aren't sensitive enough to obscure from source control.
With a consistent convention for your configuration, you can maintain good separation of application and configuration values without overusing process.env
.
Basic usage
Configuration is loaded from a directory, typically config
at the root of your project. Each file in the directory is loaded with require
and the file name is used to namespace the configuration values.
Additionally, an env
directory is supported inside of config
. Each environment can be represented here as a single file which will take priority over any previously-determined configuration values.
const config = require('@nautilus/config')('./config')
Given the following directory structure:
config
├── env
│ ├── development.js
│ ├── local.js
│ └── production.js
├── db.js
├── clients.js
└── security.js
The resulting configuration object will be shaped as:
{
db: {},
clients: {},
security: {}
}
Environment configuration
Environment configuration is based on process.env
values when the configuration is loaded. For simple setups, process.env.NODE_ENV
can be used to give you a production
value in your deployment and a development
value locally. For more advanced needs, process.env.DEPLOY_ENV
can be used with any number of values in order to leave NODE_ENV
untouched for CI purposes.
If the current environment cannot be determined, development
is used by default.
Environment configuration should be structured as a single file since it will be merged over the top of the base configuration object that is built.
Given the base configuration located at env/db.js
:
module.exports = {
host: 'atlas.mongodb.com',
poolSize: 10,
ssl: true
}
Your environment configuration should be structured as follows:
module.exports = {
db: {
host: 'beta.mongodb.com'
}
}
Which results in the following configuration:
{
db: {
host: 'beta.mongodb.com',
poolSize: 10,
ssl: true
}
}
Local development
If a file exists at env/local.js
it will take precedence over any environment configuration that is loaded. This file should be excluded from source control and will allow you to quickly change configuration during development without modifying files that are committed to source control.
Sensitive data
Any sensitive data including private API keys or credentials should be kept out of version control. This should be done whether or not your repo is public to mitigate the risk of compromising any credentials.
Best practice is to reference an environment variable that can be encrypted on your deployment platform and passed to the running application. For example, configuration for your Mongo connection might look as follows in config/db.js
:
module.exports = {
host: 'atlas.mongodb.com',
username: process.env.MONGO_USER,
password: process.env.MONGO_PASS
}
When running your application locally, take advantage of your config/env/local.js
file to store the actual username and password. The benefit here is that you don't have to worry about accidentally committing sensitive data to version control.
Optional dotenv
integration
If the dotenv
package is installed it will be used to populate your process.env
values. This is to facilitate compatibility with development flows that include the creation of a .env
file such as ZEIT's Now platform.
You may continue using local.js
to override values during local development but you will get the added benefit of having these values populated "for free" if you are using now dev
or now secrets pull
.
If you aren't using dotenv
, it is not required as a dependency. It is recommended that you use the file structures outlined above to organize your secrets and environment overrides.
Options
Environment
By default the environment will be determined based on the values of either process.env.DEPLOY_ENV
or process.env.NODE_ENV
. If neither environment variable are set, development
is used.
To override this manually, pass any value to the env
configuration key:
makeConfig('./config', { env: 'beta' })
Flat loader
For some projects, a simpler configuration structure is needed. An example may be an application built using the Webpack EnvironmentPlugin The flat configuration loader is built just for cases like this.
makeConfig('./config', { flat: true })
When using the flat loader, only three files are loaded from your configuration directory. A default.js
file is used as your base, a file corresponding to the current environment, and a local.js
if present. Each file is expected to have a flat representation of your configuration:
config
├── local.js
├── beta.js
├── production.js
└── default.js
In the above example, your default.js
might look like this:
module.exports = {
API_URI: 'https://my.api/v1/',
API_KEY: process.env.API_KEY
}
Ignore local configuration
In some cases you may want to ignore local configuration, even when it is present. The most common case for this is when running tests locally to ensure that secrets are not available. By default, if the env
is test
, local configuration is ignored. This should work out of the box for testing frameworks such as Jest which automatically set the NODE_ENV
to test
.
If necessary you may override the default behaviour with:
makeConfig('./config', { ignoreLocal: false }) // always consider `local.js` regardless of environment