🚨 v7 requires Node version 18.20.4 or higher! 🚨
good-env
provides a more intuitive way to interface with environment variables for node apps. Reasoning
about raw strings is OK for some things but for non-trivial applications, booleans, numbers, lists or even
the existence (or non-existence) of environment configurations can play a key role in how an application behaves. Lastly, good-env
has no production dependencies.
$ npm install good-env --save
With normal process.env
$ export HOST=localhost
$ export SECRET=shhh
$ export FOO=10
$ export A_TRUE_VAL=true
$ export A_FALSE_VAL=false
$ export LIST=foo,bar,bang
$ export ENDPOINT=https://foo.com
$ node
> process.env.FOO
'10'
> process.env.A_TRUE_VAL
'true'
> process.env.A_FALSE_VAL
'false'
> process.env.LIST
'foo,bar,bang'
>
Using good-env
const env = require('good-env')
env.getNumber('FOO') // 10
env.getBool('A_TRUE_VAL') // true
env.getBool('A_FALSE_VAL') // false
Warning Checking the existence of a boolean value which resolves to
false
will returntrue
becauseok()
doesn't give you a value.
export A_BOOL_VAL=false
env.ok('A_BOOL_VAL') // true
Specify defaults
env.get('NOT_SET', 'foo') // 'foo'
Batch Gets
env.getAll(['SECRET', 'HOST']) // ['shhh', 'localhost']
// defaults work here too
env.getAll({
A_SECRET: 'lolz',
HOST: null // null means no default
}) // { A_SECRET: 'lolz', HOST: 'localhost' }
Use the first available environment variable
// old and busted
const host = process.env.THE_HOST || process.env.HOST // 'localhost'
// new hotness
const host = env.get(['THE_HOST', 'HOST']) // 'localhost'
// works with defaults
const host = env.get(['THE_HOST', 'A_HOST'], 'localhost') // 'localhost'
Lists
env.getList('LIST') // ['foo', 'bar', 'bang']
env.getList('LIST_NOT_SET') // []
Number Lists
$ export LIST=1,2,3
process.env.LIST // '1,2,3'
env.list('LIST', { cast: 'number' }) // [1, 2, 3]
Sometimes you just need to know if something exists
env.ok('NOT_SET') // false
env.ok('FOO') // true
// works with multiple arguments.
// Returns true if ALL keys exist
env.ok('FOO', 'BAR') // true
env.ok('FOO', 'BAR', 'NOT_SET') // false
Use .assert(item1, item2...)
to check the existence and/or type of a few items at once
Note: If any variable passed to assert()
doesn't exist or is otherwise
invalid, an error will be thrown.
env.assert(
// Will ensure 'HOSTNAME' exists
'HOSTNAME',
// Will ensure 'PORT' both exists and is a number
{ 'PORT': { type: 'number' }},
// Will ensure 'INTERVAL' exists, it's a number and its value is greater
// than or equal to 1000
{ 'INTERVAL': { type: 'number', ok: s => s >= 1000 }}
// ... any number of arguments
)
Fetch AWS Credentials
const {
awsKeyId,
awsSecretAccessKey,
awsRegion,
} = env.getAWS();
// Use a default region
const {
awsKeyId,
awsSecretAccessKey,
awsRegion,
} = env.getAWS({ region: 'region' });
Fetch URL
objects from url strings
env.getUrl('ENDPOINT')
/*
{
httpOk: true,
href: 'https://foo.com/',
raw: URL {
href: 'https://foo.com/',
origin: 'https://foo.com',
protocol: 'https:',
username: '',
password: '',
host: 'foo.com',
hostname: 'foo.com',
port: '',
pathname: '/',
search: '',
searchParams: URLSearchParams {},
hash: ''
}
}
*/
env.getUrl('FAKE_ENDPOINT') // null
// It works with defaults
env.getUrl('FAKE_ENDPOINT', 'http://localhost:3000')
/*
{
httpOk: true,
href: 'http://localhost:3000/',
raw: URL {
href: 'http://localhost:3000/',
origin: 'http://localhost:3000',
protocol: 'http:',
username: '',
password: '',
host: 'localhost:3000',
hostname: 'localhost',
port: '3000',
pathname: '/',
search: '',
searchParams: URLSearchParams {},
hash: ''
}
}
*/
Why would one use env.getUrl()
if one just wishes to grab the url string value? The best reason to use getUrl()
and grab the href
property is that env.get()
doesn't care about the format of the value. Using getUrl()
will ensure the url is properly formatted and return a null
value if it isn't. In practice, having an invalid url is the same as having no value at all. Then again, it's your code. Do what you want!
As of now, http
, redis
and postgresql
are the only supported protocols. Other protocols will return null
. I'm not against adding new protocol support, but these are the ones that seemed most obvious to me. If you want other protocols supported, I'd recommend making a PR. You may create an issue, but I can't guarantee when I'll get around to implementation.
env.num() ==> env.getNumber()
env.bool() ==> env.getBool()
env.list() ==> env.getList()
env.url() ==> env.getUrl()