@strattadb/environment
Version:
Environment variable configuration for Node.js made easy.
433 lines (308 loc) • 12.6 kB
Markdown
# environment
[](https://dev.azure.com/strattadb/environment/_build?definitionId=1)
[](https://www.npmjs.com/package/@strattadb/environment)
[](https://codecov.io/gh/strattadb/environment)
[](https://david-dm.org/strattadb/environment)
[](CONTRIBUTING.md)
Environment variable configuration for Node.js made easy.
## The problem
A lot of applications need to ensure that some environment variables are
set from the beginning, and checking if the value in `process.env` is `undefined`
every time is needed gets tedious very fast.
`environment` allows applications to ensure required env variables are set and are valid according
to _your_ definition of valid. See [how to use it](#usage).
## Table of Contents
- [environment](#environment)
- [The problem](#the-problem)
- [Table of Contents](#table-of-contents)
- [Installation](#installation)
- [Usage](#usage)
- [Examples](#examples)
- [API](#api)
- [`makeEnv(schema: Schema, processEnv?: { [key: string]: string | undefined }): Env`](#makeenvschema-schema-processenv--key-string-string--undefined--env)
- [Parsers](#parsers)
- [`parsers.string(value: string): string`](#parsersstringvalue-string-string)
- [`parsers.boolean(value: string): boolean`](#parsersbooleanvalue-string-boolean)
- [`parsers.integer(value: string): number`](#parsersintegervalue-string-number)
- [`parsers.float(value: string): number`](#parsersfloatvalue-string-number)
- [`parsers.email(value: string): string`](#parsersemailvalue-string-string)
- [`parsers.url(value: string): string`](#parsersurlvalue-string-string)
- [`parsers.ipAddress(value: string): string`](#parsersipaddressvalue-string-string)
- [`parsers.port(value: string): number`](#parsersportvalue-string-number)
- [`parsers.whitelist(whitelistedValues: string[]): Parser<string>`](#parserswhitelistwhitelistedvalues-string-parserstring)
- [`parsers.regex(pattern: Regex): Parser<string>`](#parsersregexpattern-regex-parserstring)
- [`parsers.array<T>({ parser: Parser<T>, separator?: string }): Parser<T>`](#parsersarrayt-parser-parsert-separator-string--parsert)
- [`parsers.positiveInteger(value: string): number`](#parserspositiveintegervalue-string-number)
- [`parsers.nonPositiveInteger(value: string): number`](#parsersnonpositiveintegervalue-string-number)
- [`parsers.negativeInteger(value: string): number`](#parsersnegativeintegervalue-string-number)
- [`parsers.nonNegativeInteger(value: string): number`](#parsersnonnegativeintegervalue-string-number)
- [Recipes](#recipes)
- [Making environment variables required](#making-environment-variables-required)
- [Specifying a default value for when the env variable is not required](#specifying-a-default-value-for-when-the-env-variable-is-not-required)
- [Providing a custom parser](#providing-a-custom-parser)
- [Usage with Dotenv](#usage-with-dotenv)
- [Providing your own processEnv object](#providing-your-own-processenv-object)
- [FAQ](#faq)
- [Where should I call `makeEnv` in my application?](#where-should-i-call-makeenv-in-my-application)
- [Does it support changing env variables dynamically?](#does-it-support-changing-env-variables-dynamically)
- [Can I use the `debug` module with `environment`?](#can-i-use-the-debug-module-with-environment)
- [Can I have more than one env object per application?](#can-i-have-more-than-one-env-object-per-application)
- [Node.js support](#nodejs-support)
- [Contributing](#contributing)
- [Maintainers](#maintainers)
- [Who's using environment](#whos-using-environment)
- [Related libraries](#related-libraries)
- [License](#license)
## Installation
With [Yarn](https://yarnpkg.com/):
```bash
yarn add @strattadb/environment
```
or with npm:
```bash
npm install @strattadb/environment
```
## Usage
An example `env.js` file:
```javascript
// env.js
import { makeEnv, parsers } from '@strattadb/environment';
const env = makeEnv({
nodeEnv: {
parser: parsers.whitelist(['production', 'development', 'test']),
required: true,
envVarName: 'NODE_ENV',
},
port: {
parser: parsers.port,
required: false,
defaultValue: 4000,
envVarName: 'PORT',
},
});
export default env;
```
Now in a file:
```javascript
// otherFile.js
import env from './env';
console.log(env.nodeEnv); // development
console.log(typeof env.nodeEnv); // string
console.log(env.port); // 4000
console.log(typeof env.port); // number
```
## Examples
- [Simple HTTP server](examples/server)
## API
### `makeEnv(schema: Schema, processEnv?: { [key: string]: string | undefined }): Env`
Ensures required env variables are present and returns an env object.
Supports passing a processEnv object as the second argument.
If it's not passed, it uses `process.env`.
This object will be used to look up the environment variables.
- **Schema**:
- **\[key: string\]**: `object` - The `key` will be accessible
in the returning env object.
- **parser**: `function` - A function that takes a string and can return anything.
The return value will be accesible in `env[key]`.
If the argument is not valid, it should throw.
- **required**: `boolean` - Whether or not the env variable is required.
If the value `true` and the env variable is not set, it'll throw.
If the value is `false` it'll look for the env variable in `process.env`,
if isn't set, it'll use `defaultValue`.
- **defaultValue**: `any?` - Only valid if `required: false`.
This is the default value of the env variable if it's not set.
It won't be parsed or validated.
- **envVarName**: `string` - The name of the env variable to look up
(`process.env[envVarName]`).
- **description**: `string?` - Helper text describing the variable.
- **Env**:
- **\[key: string\]**: `any` - The keys are the same as the ones in the schema.
### Parsers
#### `parsers.string(value: string): string`
Trivial parser. It doesn't do any validation.
#### `parsers.boolean(value: string): boolean`
Ensures the value is a truthy or falsy value.
Truthy values: `'true'`, `'1'`, `'yes'`, `'on'`.
Falsy values: `'false'`, `'0'`, `'no'`, `'off'`.
#### `parsers.integer(value: string): number`
Ensures the value is an integer.
#### `parsers.float(value: string): number`
Ensures the value is a float.
#### `parsers.email(value: string): string`
Ensures the value is an email.
#### `parsers.url(value: string): string`
Ensures the value is a url.
#### `parsers.ipAddress(value: string): string`
Ensures the value is an IP address.
#### `parsers.port(value: string): number`
Ensures the value is a port.
#### `parsers.whitelist(whitelistedValues: string[]): Parser<string>`
Takes a list of valid values and returns a parser that
ensures the value is in the whitelist.
Example:
```javascript
import { makeEnv, parsers } from '@strattadb/environment';
const env = makeEnv({
color: {
parser: parsers.whitelilst(['red', 'blue', 'green']),
required: true,
envVarName: 'COLOR',
},
});
```
#### `parsers.regex(pattern: Regex): Parser<string>`
Takes a regex and returns a parser that
ensures the value matches the pattern.
Example:
```javascript
import { makeEnv, parsers } from '@strattadb/environment';
const env = makeEnv({
color: {
parser: parsers.regex(/^green$/),
required: true,
envVarName: 'COLOR',
},
});
```
#### `parsers.array<T>({ parser: Parser<T>, separator?: string }): Parser<T>`
Takes a parser and returns a parser that parses a list of values.
The default value separator is ','.
Example:
```javascript
import { makeEnv, parsers } from '@strattadb/environment';
const env = makeEnv({
color: {
parser: parsers.array({ parser: parsers.integer }),
required: true,
envVarName: 'FAVORITE_NUMBERS',
},
});
```
#### `parsers.positiveInteger(value: string): number`
Ensures the value is a positive integer.
#### `parsers.nonPositiveInteger(value: string): number`
Ensures the value is a non-positive integer.
#### `parsers.negativeInteger(value: string): number`
Ensures the value is a negative integer.
#### `parsers.nonNegativeInteger(value: string): number`
Ensures the value is a non-negative integer.
## Recipes
### Making environment variables required
If `required` is `true` and the environment variable isn't set,
it'll throw:
```javascript
// env.js
import { makeEnv, parsers } from '@strattadb/environment';
const env = makeEnv({
notSet: {
parser: parsers.string,
required: true,
envVarName: 'NOT_SET',
},
});
```
```bash
node env.js
EnvironmentVariableError: NOT_SET is required but is not set
at ...
...
```
### Specifying a default value for when the env variable is not required
If the env variable is not required you must specify a default value:
```javascript
import { makeEnv, parsers } from '@strattadb/environment';
const env = makeEnv({
port: {
parser: parsers.port,
required: false,
defaultValue: 4000,
envVarName: 'PORT',
},
});
console.log(env.port); // 4000
```
### Providing a custom parser
A parser function must take a string value and can return anything.
The value you return is what you'll get in the env object.
If the value is not valid you should throw an error:
```javascript
import { makeEnv, parsers } from '@strattadb/environment';
const env = makeEnv({
someValue: {
parser: (value) => {
if (value === 'forbiddenValue') {
throw new Error('value is forbidden');
}
return value;
},
required: true,
envVarName: 'SOME_VALUE',
},
});
```
### Usage with [Dotenv](https://github.com/motdotla/dotenv)
A common pattern is to load the env variables from a file using dotenv
and then ensuring that the required variables are actually present:
```javascript
const dotenv = require('dotenv');
dotenv.config(); // Loads env variables from `.env` file to `process.env`.
const { makeEnv, parsers } = require('@strattadb/environment');
const env = makeEnv({
secretToken: {
parser: parsers.string,
required: true,
envVarName: 'SECRET_TOKEN',
},
});
```
### Providing your own processEnv object
By default, `makeEnv` uses `process.env` to look up and get environment variables,
but you can pass you own processEnv object as the second argument that will
be used instead of `process.env`:
```javascript
import { makeEnv, parsers } from '@strattadb/environment';
const env = makeEnv(
{
hello: {
parser: parsers.string,
required: true,
envVarName: 'HELLO',
},
},
{
HELLO: 'WORLD',
},
);
```
## FAQ
### Where should I call `makeEnv` in my application?
The best place to create the env object is very early
in your application startup.
Everything before you call `makeEnv` with your schema will
not be guaranteed to have your required env variables.
### Does it support changing env variables dynamically?
No, when you create an env object it will read the value of
`process.env` at that time. After that, if anything makes
changes to `process.env`, it will not be reflected in the
env object.
### Can I use the [`debug`](https://github.com/visionmedia/debug) module with `environment`?
Yes! Set `DEBUG=environment`.
### Can I have more than one env object per application?
Yes! You can have as many env objects as you want!
## Node.js support
Node.js version 10 or higher. Every version of this library
is tested in Node.js 10, 12, 14 and 15.
## Contributing
PRs, feature requests, bug reports, and any kind of contributions are welcome!
See [CONTRIBUTING.md](CONTRIBUTING.md).
## Maintainers
- [Diego Stratta](https://github.com/strattadb)
## Who's using environment
- [origen](https://github.com/origen-chat/api)
## Related libraries
- [Convict](https://github.com/mozilla/node-convict)
- [Envalid](https://github.com/af/envalid)
- [require-environment-variables](https://github.com/bjoshuanoah/require-environment-variables)
- [Dotenv](https://github.com/motdotla/dotenv)
## License
[MIT](LICENSE)