env-var
solution for loading and sanatizing environment variables in node.js with correct typings
Install
npm install env-var --save
Example
In the example below we read the environment variable PARALLEL_LIMIT, ensure
it is set, and parse it to an integer.
var PARALLEL_LIMIT = env.get('PARALLEL_LIMIT').required().asIntPositive();
Here's what each piece of this code means:
- If PARALLEL_LIMIT is not set required() will raise an exception.
- If it is set, but not a positive integer asIntPositive() will raise an
exception.
- If #1 and #2 do not raise an exception, the number will be returned as a
valid JavaScript number type.
TypeScript
To use with TypeScript, just import it like this:
import * as env from 'env-var';
const stringVar = env.get('STRING').required().asString();
Overview
Over time it became apparent that parsing environment variables is a
repetitive task, and testing code that relies on them is cumbersome unless
using an inversion of control system for declaring modules so we can inject a
fake process.env.
Take this example:
var assert = require('assert');
assert.notEqual(
process.env.MAX_BATCH_SIZE,
undefined,
'MAX_BATCH_SIZE environment variable must be set'
);
var MAX_BATCH_SIZE = parseInt(process.env.MAX_BATCH_SIZE, 10);
assert(
typeof MAX_BATCH_SIZE === 'number' && !isNaN(MAX_BATCH_SIZE),
'MAX_BATCH_SIZE env var must be a valid number'
);
With env-var the example above can be written cleanly as:
var env = require('env-var');
var MAX_BATCH_SIZE = env.get('MAX_BATCH_SIZE').required().asInt();
When it comes to testing code that relies on environment variables this is also
great since you can mock out env-var using proxyquire to easily alter
results returned without having to share state via process.env. A
demonstration of this is at the bottom of the README.
API
env.get([varname, [default]])
You can call this function 3 different ways:
- Calling without arguments will return the entire process.env Object.
- Calling with varname will return a variable instance with utilities for
parsing variables and is detailed below.
- Calling with varname, and default will return the value for varname
set on process.env, or if the variable is not set default run through the
variable instance functions as though it was set on process.env.
env.EnvVarError
This is the error class used to represent errors raised by this module. You can
use it like so:
const env = require('env-var')
try {
env.get('MISSING_VARIABLE').required().asString()
throw new Error('some other error')
} catch (e) {
if (e instanceof env.EnvVarError) {
console.log('we got an env-var error', e)
} else {
console.log('we got some error that wasn\'t an env-var error', e)
}
}
variable
A returned variable has the following functions defined for parsing to the
required format.
required()
Ensure the variable is set on process.env. If the variable is not set, then
this function will throw an EnvVarError
. Typically you will use this during
the initialisation of your program in order to fail fast if a required variable
is not set.
For example:
const env = require('env-var')
const PORT = env.get('PORT').required().asIntPositive()
app.listen(PORT)
asInt()
Attempt to parse the variable to an integer. Throws an exception if parsing
fails. This is a strict check, meaning that if the process.env value is "1.2",
an exception will be raised rather than rounding up/down.
asIntPositive()
Performs the same task as asInt(), but also verifies that the number is
positive (greater than zero).
asIntNegative()
Performs the same task as asInt(), but also verifies that the number is
negative (less than zero).
asFloat()
Attempt to parse the variable to a float. Throws an exception if parsing fails.
asFloatPositive()
Performs the same task as asFloat(), but also verifies that the number is
positive (greater than zero).
asFloatNegative()
Performs the same task as asFloat(), but also verifies that the number is
negative (less than zero).
asString()
Return the variable value as a String. Throws an exception if value is not a
String. It's highly unlikely that a variable will not be a String since all
process.env entries you set in bash are Strings by default.
asBool()
Attempt to parse the variable to a Boolean. Throws an exception if parsing
fails. The var must be set to either "true", "false" (upper or lowercase),
0 or 1 to succeed.
asBoolStrict()
Attempt to parse the variable to a Boolean. Throws an exception if parsing
fails. The var must be set to either "true" or "false" (upper or lowercase) to
succeed.
asJson()
Attempt to parse the variable to a JSON Object or Array. Throws an exception if
parsing fails.
asJsonArray()
The same as asJson but checks that the data is a JSON Array, e.g [1,2].
asJsonObject()
The same as asJson but checks that the data is a JSON Object, e.g {a: 1}.
asArray([delimiter])
Reads an environment variable as a string, then splits it on each occurence of
the specified delimiter. By default a comma is used as the delimiter. For
example a var set to "1,2,3" would become ['1', '2', '3'].
asUrlString()
Verifies that the variable is a valid URL string and returns that string. Uses
is-url
to perform validation, so check that module for validation rules.
asUrlObject()
Verifies that the variable is a valid URL string, then parses it using
url.parse
from the Node.js core url
module and returns the parsed Object.
See the Node.js docs for more info
Example
const env = require('env-var');
process.env.STRING = 'test';
process.env.INTEGER = '12';
process.env.BOOL = 'false';
process.env.JSON = '{"key":"value"}';
process.env.COMMA_ARRAY = '1,2,3';
process.env.DASH_ARRAY = '1-2-3';
const allVars = env.get();
const stringVar = env.get('STRING').required().asString();
const intVar = env.get('INTEGER').asInt();
const floatVar = env.get('FLOAT', '23.2').asFloat();
const boolVar = env.get('BOOL').required().asBool();
const jsonVar = env.get('JSON').asJson();
const commaArray = env.get('COMMA_ARRAY').asArray();
const commaArray = env.get('DASH_ARRAY').asArray('-');
Testing Overview
When testing code that relies on environment variables sometimes we need to
mock out/set the environment variables. Having calls to process.env strewn
throughout a test is and can get confusing and modifies global state (not good).
It's better to use env-var and its built-in mock()
function. Using mock()
will allow you to create a mocked version of env-var which will use a literal
object instead of using process.env. You can use this mocked version with
something like proxyquire
. For example:
var env = require('env-var');
exports.concat = function (envVarToGet) {
return envVarToGet + ' ' + env.get(envVarToGet).required().asString();
};
var expect = require('chai').expect;
var proxyquire = require('proxyquire');
var env = require('env-var');
describe('concat.js', function () {
var mod;
beforeEach(function () {
mod = proxyquire('./concat', {
'env-var': env.mock({
HELLO: 'WORLD'
})
});
});
describe('#concat', function () {
it('should combine our var name and its returned value', function () {
expect(mod.concat('HELLO')).to.equal('HELLO WORLD');
});
});
});
Contributors
Contributing
Contributions are welcomed. If you'd like to discuss an idea open an issue, or a
PR with an initial implementation.
If you want to add a new type it's pretty easy. Add a file to lib/accessors
,
with the name of the type e.g add a file named number-zero.js
into that folder
and populate it with code following this structure:
module.exports = function numberZero (raiseError, environmentValue) {
const val = parseInt(environmentValue)
if (val === 0) {
return ret;
} else {
raiseError('should be zero')
}
}
The env-var
module will auto load this new file and add it to the public
exports with the name in camelcase format asNumberZero
env.get('SOME_NUMBER').asNumberZero()