env-var
- ๐ Lightweight. Zero dependencies and just ~4.7kB when minified!
- ๐งน Cleaner code, as shown here
- ๐ซ Fail fast if your environment is misconfigured
- ๐ฉโ๐ป Friendly error messages and example values improve developer experience
- ๐ TypeScript support provides compile time safety and better developer experience
Install
Note: requires Node version 8 or later.
npm
npm install env-var --save
yarn
yarn add env-var
Usage
In the example below we read the environment variable DB_PASSWORD and call
some functions to verify it satisfies our program's needs.
const env = require('env-var');
const PASSWORD = env.get('DB_PASSWORD')
.required()
.convertFromBase64()
.asString();
const PORT = env.get('PORT').default('5432').asPortNumber()
TypeScript
import * as env from 'env-var';
const PORT: number = env.get('PORT').required().asIntPositive();
Usage with dotenv
There is no tight coupling between env-var
and dotenv. Just npm install dotenv
and use it whatever way you're used to. This loose coupling is a good thing
since it reduces package bloat - only install what you need!
You can use dotenv
with env-var
via a require()
calls in your code or
preloading it with the --require
or -r
flag in the node
CLI.
Both examples below assume you have a .env
file in your repository and it
contains a line similar to MY_VAR=a-string-value!
.
Load dotenv via require()
This is per the default usage described by dotenv's README.
require('dotenv').config()
const env = require('env-var')
const myVar = env.get('MY_VAR').asString()
Preload dotenv via CLI Args
This is per the preload section
of the dotenv README. Run the following code by using the
node -r dotenv/config your_script.js
command.
const env = require('env-var')
const myVar = env.get('MY_VAR').asString()
Logging
Logging is disabled by default in env-var
to prevent accidentally logging
secrets.
To enable logging you need to create an env-var
instance using the from()
function that the API provides and pass it a logger. A built-in logger is
available, but a custom logger is also supported.
Always exercise caution when logging environment variables!
Using the Built-in Logger
The built-in logger will print logs unless NODE_ENV
is set to either prod
or production
.
const { from, logger } = require('env-var')
const env = from(process.env, {}, logger)
const API_KEY = env.get('API_KEY').required().asString()
Here's output from the built-in logger that can be seen by running
examples/logging.js included in this repository:
Using a Custom Logger
If you're using a logging solution such as pino
this feature is useful to
filter logs based on log levels, e.g env-var
logs can be enabled for trace
logging only.
const pino = require('pino')()
const customLogger = (varname, str) => {
log.trace(`env-var log (${varname}): ${str}`)
}
const { from } = require('env-var')
const env = from(process.env, {}, customLogger)
const API_KEY = env.get('API_KEY').required().asString()
API
Structure:
This function is useful if you're not in a typical Node.js environment, or for
testing. It allows you to generate an env-var
instance that reads from the
given values
instead of the default process.env
Object.
const env = require('env-var').from({
API_BASE_URL: 'https://my.api.com/'
})
const apiUrl = env.get('API_BASE_URL').asUrlString()
When calling env.from()
you can also pass an optional parameter containing
custom accessors that will be attached to any variables returned by that
env-var
instance. This feature is explained in the
extraAccessors section of these docs.
Logging can be enabled by passing a logger function that matches the signature:
function yourLoggerFn (varname, str) {
}
get(varname)
This function has two behaviours:
- Calling with a string argument will make it read that value from the environment
- If no string argument is passed it will return the entire environment object
Examples:
const env = require('env-var')
const limit = env.get('MAX_CONNECTIONS').asIntPositive()
const allVars = env.get()
variable
A variable is returned by calling env.get(varname)
. It exposes the following
functions to validate and access the underlying value, set a default, or set
an example value:
example(string)
Allows a developer to provide an example of a valid value for the environment
variable. If the variable is not set (and required()
was called), or the
variable is set incorrectly this will be included in error output to help
developers diagnose the error.
For example:
const env = require('env-var')
const ADMIN_EMAIL = env.get('ADMIN_EMAIL')
.required()
.example('admin@example.com')
.asString()
If ADMIN_EMAIL was not set this code would throw an error similar to that
below to help a developer diagnose the issue:
env-var: "ADMIN_EMAIL" is a required variable, but it was not set. An example
of a valid value would be "admin@example.com"
default(string)
Allows a default value to be provided for use if the desired environment
variable is not set in the program environment.
Example:
const env = require('env-var')
const POOL_SIZE = env.get('POOL_SIZE').default('10').asIntPositive()
required(isRequired = true)
Ensure the variable is set on process.env. If the variable is not set, or is
set to an empty value, this function will cause an EnvVarError
to be thrown
when you attempt to read the value using asString
or a similar function.
The required()
check can be bypassed by passing false
, i.e
required(false)
Example:
const env = require('env-var')
const NODE_ENV = env.get('NODE_ENV').asString()
const PORT = env.get('PORT').required().asPortNumber()
const SECRET = env.get('SECRET').required(NODE_ENV === 'production').asString()
convertFromBase64()
It's a common need to set an environment variable in base64 format. This
function can be used to decode a base64 environment variable to UTF8.
For example if we run the script script below, using the command DB_PASSWORD= $(echo -n 'secret_password' | base64) node
, we'd get the following results:
console.log(process.env.DB_PASSWORD)
const dbpass = env.get('DB_PASSWORD').convertFromBase64().asString()
asPortNumber()
Converts the value of the environment variable to an integer and verifies it's
within the valid port range of 0-65535. As a result well known ports are
considered valid by this function.
asEnum(validValues: string[])
Converts the value to a string, and matches against the list of valid values.
If the value is not valid, an error will be raised describing valid input.
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 or equal to zero).
asIntNegative()
Performs the same task as asInt(), but also verifies that the number is
negative (less than or equal to 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 or equal to zero).
asFloatNegative()
Performs the same task as asFloat(), but also verifies that the number is
negative (less than or equal to 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: string])
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']. Example outputs for
specific values are:
- Reading
MY_ARRAY=''
results in []
- Reading
MY_ARRAY='1'
results in ['1']
- Reading
MY_ARRAY='1,2,3'
results in ['1', '2', '3']
asUrlString()
Verifies that the variable is a valid URL string and returns the validated
string. The validation is performed by passing the URL string to the
Node.js URL constructor.
Note that URLs without paths will have a default path /
appended when read, e.g.
https://api.acme.org
would become https://api.acme.org/
. Always use URL
safe utilities included in the
Node.js URL module to create
valid URL strings, instead of error prone string concatenation.
asUrlObject()
Verifies that the variable is a valid URL string using the same method as
asUrlString()
, but instead returns the resulting URL instance. For details
see the Node.js URL docs.
asRegExp([flags: string])
Read in the variable and construct a RegExp
instance using its value. An optional flags
argument is supported. The string
passed for flags
is passed as the second argument to the RegExp
constructor.
EnvVarError()
This is the error class used to represent errors raised by this module. Sample
usage:
const env = require('env-var')
let value = null
try {
value = 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)
}
}
Examples
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').default('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('-');
const enumVal = env.get('ENVIRONMENT').asEnum(['dev', 'test', 'live'])
accessors
A property that exposes the built-in accessors that this module uses to parse
and validate values. These work similarly to the asString() and other
accessors exposed on the variable type documented above, however they accept
a String as their first argument, e.g:
const env = require('env-var')
const myJsonDirectAccessor = env.accessors.asJson(process.env.SOME_JSON)
const myJsonViaEnvVar = env.get('SOME_JSON').asJson()
All of the documented asX() accessors above are available. These are useful
if you need to build a custom accessor using the extraAccessors functionality
described below.
When calling from()
you can also pass an optional parameter containing
additional accessors that will be attached to any variables gotten by that
env-var
instance.
Accessor functions must accept at least one argument:
{*} value
: The value that the accessor should process.
Important: Do not assume that value
is a string!
Example:
const { from } = require('env-var')
process.env.ADMIN = 'admin@example.com'
const env = from(process.env, {
asEmail: (value) => {
const split = String(value).split('@')
if (split.length !== 2) {
throw new Error('must contain exactly one "@"')
}
return value
}
})
let validEmail = env.get('ADMIN').asEmail()
The accessor function may accept additional arguments if desired; these must be
provided explicitly when the accessor is invoked.
For example, we can modify the asEmail()
accessor from above so that it
optionally verifies the domain of the email address:
const { from } = require('env-var')
process.env.ADMIN = 'admin@example.com'
const env = from(process.env, {
asEmail: (value, requiredDomain) => {
const split = String(value).split('@')
if (split.length !== 2) {
throw new Error('must contain exactly one "@"')
}
if (requiredDomain && (split[1] !== requiredDomain)) {
throw new Error(`must end with @${requiredDomain}`)
}
return value
}
})
let validEmail = env.get('ADMIN').asEmail()
let invalidEmail = env.get('ADMIN').asEmail('github.com')
This feature is also available for TypeScript users. The ExtensionFn
type is
exposed to help in the creation of these new accessors.
import { from, ExtensionFn, EnvVarError } from 'env-var'
process.env.ADMIN = 'admin@example.com'
const asEmail: ExtensionFn<string> = (value) => {
const split = String(value).split('@')
if (split.length !== 2) {
throw new Error('must contain exactly one "@"')
}
return value
}
const env = from(process.env, {
asEmail
})
env.get('ADMIN').asEmail()
You can view an example of composing built-in accessors made available by
env.accessors
in an extra accessor at examples/custom-accessor.js.
Contributing
Contributions are welcomed and discussed in the CONTRIBUTING.md
file in this
repo. If you'd like to discuss an idea open an issue, or a PR with an initial
implementation.
Contributors
- @aautio
- @caccialdo
- @ChibiBlasphem
- @DigiPie
- @evanshortiss
- @gabrieloczkowski
- @hhravn
- @ineentho
- @itavy
- @joh-klein
- @MikeyBurkman
- @pepakriz
- @rmblstrp
- @shawnmclean
- @todofixthis
- @xuo