Loads environment variables from `.env.[development|test|production][.local]` files
npm install @codexsoft/dotenv-flow
This repo is a fork of original dotenv-flow package written by Dan Kerimdzhanov, with some extra features like optional allowing load .env.local in test environment and TypeScript typings out of the box.
dotenv is a zero-dependency npm module that loads environment variables from a .env file into process.env.
dotenv-flow extends dotenv adding the ability to have multiple .env files like .env.development, .env.test and .env.production, also allowing defined variables to be overwritten individually in the appropriate .env.local file.
Storing configuration in _environment variables_ separate from code and grouping them by environments like _development_, _test_ and _production_ is based on The Twelve-Factor App methodology.



Using NPM:
``sh`
$ npm install @codexsoft/dotenv-flow --save
Using Yarn:
`sh`
$ yarn add @codexsoft/dotenv-flow
As early as possible in your application, require and configure dotenv-flow.
`js`
require('@codexsoft/dotenv-flow').config();
After this, you can access all the environment variables you have defined in your .env files through process.env..
For example, let's suppose that you have the following .env* files in your project:
`sh.env
DATABASE_HOST=127.0.0.1
DATABASE_PORT=27017
DATABASE_USER=default
DATABASE_PASS=
DATABASE_NAME=my_app
`
`sh.env.local
DATABASE_USER=local-user
DATABASE_PASS=super-secret
`
`sh.env.development
DATABASE_NAME=my_app_dev
`
`sh.env.test
DATABASE_NAME=my_app_test
`
`sh.env.production
DATABASE_NAME=my_app_prod
`
`sh.env.production.local
DATABASE_HOST=10.0.0.32
DATABASE_PORT=27017
DATABASE_USER=devops
DATABASE_PASS=1qa2ws3ed4rf5tg6yh
DATABASE_NAME=application_storage
`
`js
// your_script.js
require('dotenv-flow').config();
console.log('database host:', process.env.DATABASE_HOST);
console.log('database port:', process.env.DATABASE_PORT);
console.log('database user:', process.env.DATABASE_USER);
console.log('database pass:', process.env.DATABASE_PASS);
console.log('database name:', process.env.DATABASE_NAME);
`
And if you run your_script.js in the development environment, like:
`sh`
$ NODE_ENV=development node your_script.js
you'll get the following output:
`text`
database host: 127.0.0.1
database port: 27017
database user: local-user
database pass: super-secret
database name: my_app_dev
Or if you run the same script in the production environment:
`sh`
$ NODE_ENV=production node your_script.js
you'll get the following:
`text`
database host: 10.0.0.32
database port: 27017
database user: devops
database pass: 1qa2ws3ed4rf5tg6yh
database name: application_storage
Note that the .env*.local files should be ignored by your version control system (refer the Files under version control section below to learn more), and you should have the .env.production.local file only on your production deployment machine.
Actually, dotenv-flow doesn't have any predefined environment names, so you may use whatever names you want.
However, it's a good practice to use the world's universally recognized environment names like development, test, production, as well as frequently used qa or stage.
The naming convention for NODE_ENV-specific files is simply as .env.${NODE_ENV}[.local] (i.e. .env.development, .env.test, .env.production, .env.development.local, .env.production.local, etc.).
To activate specific environment run your application with predefined NODE_ENV environment variable, like:
`sh`
$ export NODE_ENV=production
$ node your_script.js
or:
`sh`
$ NODE_ENV=production node your_script.js
If you are on Windows:
`bat`
> SET NODE_ENV=production
> node your_script.js
Or even better, use cross-env to make it work independent of platform:
`sh`
$ cross-env NODE_ENV=production node your_script.js
The --node-env switch is also supported:
`sh`
$ node your_script.js --node-env=production
Alternatively, you can preload dotenv-flow using node's -r (--require) command line option.
`sh`
$ NODE_ENV=production node -r dotenv-flow/config your_script.js
or:
`sh`
$ node -r dotenv-flow/config your_script.js --node-env=production
You can also use environment variables to set configuration options when preloading the dotenv-flow/config:
`sh`
$ DOTENV_FLOW_PATH=/path/to/env-files-dir node -r dotenv-flow/config your_script.js
Refer to the dotenv-flow/config options section below to see all available options.
The main point here is not to commit production database passwords, API keys and other sensitive things to your source code repository,
but it's still nice to have default database connections, ports, hosts, etc., for development and testing purposes to keep your code clean and simple.
Understanding the above, we have the following approach:
You can keep all the fallback values in the default .env file, that (if exists) will always be loaded by default..env
Also, it is a good place to have all the application used environment variables there, thus having a reference of environment variables that are used by your application on the whole.
So it is a good reason to share the file with other developers in your team, but keep all the sensitive data on your own (or production) machine locally in the .env*.local files.
It is not necessary, but also a good practice to use NODE_ENV to control the environment to run your application in.NODE_ENV
And if you follow this practice you can keep the -specific defaults in your .env.development, .env.test, .env.production files sharing them with your team as well.NODE_ENV
Any -specific .env. file's values can also be overwritten in the appropriate .env..local file (i.e. .env.development.local, .env.test.local, .env.production.local).
Summarizing the above, you can have the following .env* files in your project:
* .env – for default (fallback) values, tracked by VCS.env.development
* – for development environment, tracked by VCS.env.test
* – for test environment, tracked by VCS.env.production
* – for production environment, tracked by VCS.env.local
* – for individual default values, ignored by VCS.env.development.local
* – for individual development environment values, ignored by VCS.env.test.local
* – for individual test environment values, ignored by VCS.env.production.local
* – for production environment values (DB passwords, API keys, etc.), ignored by VCS
Note that .env. file names may vary in your project depending on your own needs/preferences, just keep in mind that .env.local files should be untracked (ignored) by your version control system.
Here is an example of the .gitignore (or .hgignore) file entry to keep it clear:
`gitignore`local .env* files
.env.local
.env.*.local
Since multiple .env* files are loaded simultaneously, all the variables defined in these files are merged in the following order:
1) The .env file has the lowest priority. _Keep the most default (fallback) values there_;.env.local
2) The file has a priority over the .env. _Create that file if you want to overwrite the default values for your own environment-specific needs_.NODE_ENV=test
Heads up, there is an exception! In case when , .env.local is not loaded by default. You can force loading it by setting options_load_env_local_in_test_env option to true);NODE_ENV
3) -specific env files (like .env.development, .env.test, etc.) have a priority over the default .env and .env.local files. _Keep NODE_ENV-specific environment variables there_;NODE_ENV
4) -specific local env files (.env.development.local, .env.production.local, etc.) have the highest priority over all the env files. _As with .env.local, create them only if you need to overwrite NODE_ENV-specific values for your own environment-specific needs_;
5) Environment variables that are already set will not be overwritten, that means that the command line variables have a higher priority over all those defined in env files;
In addition to .env, you may also use .env.defaults to store default (fallback) values.
This may come handy e.g. when migrating from dotenv (where it is strongly advised against committing .env file to VCS).env
and you already have file used to store your local values.
In such case, you may prefer to keep using your existing .env (ignored by VCS) as your local config.env.defaults
and create additional (tracked by VCS) file which will be loaded before .env.
Then at every place .env is mentioned in the docs, read it as: ".env.defaults first, then .env".
optionsWhen preloading dotenv-flow using the node's -r switch you can use the following configuration options:
* NODE_ENV => options.node_env;DEFAULT_NODE_ENV
* => options.default_node_env;DOTENV_FLOW_PATH
* => options.path;DOTENV_FLOW_ENCODING
* => options.encoding;DOTENV_FLOW_PURGE_DOTENV
* => options.purge_dotenv;DOTENV_FLOW_SILENT
* => options.silent;DOTENV_FLOW_LOAD_ENV_LOCAL_IN_TEST_ENV
* => options.load_env_local_in_test_env;
`sh`
$ NODE_ENV=production DOTENV_FLOW_PATH=/path/to/env-files-dir node -r dotenv-flow/config your_script.js
* --node-env => options.node_env;--default-node-env
* => options.default_node_env;--dotenv-flow-path
* => options.path;--dotenv-flow-encoding
* => options.encoding;--dotenv-flow-purge-dotenv
* => options.purge_dotenv;--dotenv-flow-silent
* => options.silent;--dotenv-flow-load-env-local-in-test-env
* => options.load_env_local_in_test_env;
Don't forget to separate dotenv-flow/config-specific CLI switches with -- because they are not recognized by Node.js:
`sh`
$ node -r dotenv-flow/config your_script.js -- --dotenv-flow-encoding=latin1 --dotenv-flow-path=...
#### .config([options]) => object
The main entry point function that parses the contents of your .env files, merges the results and appends to process.env..
Also, like the original module (dotenv), it returns an object with the parsed property containing the resulting key/values or the error property if the initialization is failed.
##### options.node_envstring
###### Type: process.env.NODE_ENV
###### Default:
With the node_env option you can force the module to use your custom environment value independent of process.env.NODE_ENV:
`js`
require('@codexsoft/dotenv-flow').config({
node_env: process.argv[2] || 'development'
});
##### options.default_node_envstring
###### Type:
###### Default: _undefined_
If the NODE_ENV environment variable is not set, the module doesn't load/parse any NODE_ENV-specific files at all."development"
Therefore, you may want to use as a default environment, like:
`js`
require('@codexsoft/dotenv-flow').config({
default_node_env: 'development'
});
To be clear, just make a note that all the following initialization examples are also equivalent:
`js
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
require('@codexsoft/dotenv-flow').config();
`
`js`
require('@codexsoft/dotenv-flow').config({
node_env: process.env.NODE_ENV || 'development'
});
`js`
require('@codexsoft/dotenv-flow').config({
node_env: process.env.NODE_ENV,
default_node_env: 'development'
});
All the examples above, considers the value of process.env.NODE_ENV at first, and if it is not set, uses "development" as the value by default.
You can just choose one that looks prettier for you.
##### options.pathstring
###### Type: process.cwd()
###### Default: _(current working directory)_
With the path initialization option you can specify a path to .env* files directory:
`js`
require('@codexsoft/dotenv-flow').config({
path: '/path/to/env-files-dir'
});
If the option is not provided, the current working directory is used.
##### options.encodingstring
###### Type: "utf8"
###### Default:
You can specify the encoding for reading your files containing environment variables.
`js`
require('@codexsoft/dotenv-flow').config({
encoding: 'base64'
});
##### options.purge_dotenvboolean
###### Type: false
###### Default:
In some cases the original "dotenv" library can be used by one of the dependent
npm modules. It causes calling the original dotenv.config() that loads.env
the file from your project before you can call dotenv-flow.config().
Such cases breaks .env* files priority because the previously loaded
environment variables are treated as shell-defined thus having a higher priority.
Setting the purge_dotenv option to true can gracefully fix this issue.
`js`
require('@codexsoft/dotenv-flow').config({
purge_dotenv: true
});
##### options.silentboolean
###### Type: false
###### Default:
With this option you can suppress all the console outputs except errors and deprecation warnings.
`js`
require('@codexsoft/dotenv-flow').config({
silent: true
});
---
The following API considered as internal, but it is also exposed to give the ability to be used programmatically by your own needs.
#### .listDotenvFiles(dirname, [options]) => string[]
Returns a list of .env* filenames depending on the given options.node_env. The resulting list is ordered by the env files priority from lowest to highest.
Also, note that the .env.local file will not be listed for NODE_ENV="test", since normally you expect tests to produce the same results for everyone. To force loading that file set option load_env_local_in_test_env to true.
##### Parameters:
##### dirnamestring
###### Type:
A path to .env* files' directory.
##### [options.node_env]string
###### Type:
###### Default: _undefined_
The node environment (development/test/production/etc,).
##### Returns:
###### Type: string[]
A list of .env* filenames.
##### Example:
`js
const dotenvFlow = require('@codexsoft/dotenv-flow');
const filenames = dotenvFlow.listDotenvFiles('/path/to/project', { node_env: 'development' });
console.log(filenames); // will output the following:
// > [ '/path/to/project/.env',
// > '/path/to/project/.env.local',
// > '/path/to/project/.env.development',
// > '/path/to/project/.env.development.local' ]
`
#### .parse(filenames, [options]) => object
Parses the content of a given file(s) to use the result programmatically. Accepts a filename or a list of filenames and returns a map of the parsed key/values as an object.
When several filenames are given, the parsed variables are merged into a single object using the "overwrite" strategy.
##### Parameters:
##### filenamesstring|string[]
###### Type:
A filename or a list of filenames to parse.
##### [options.encoding]string
###### Type: "utf8"
###### Default:
An optional encoding for reading files.
##### Returns:
###### Type: object
The resulting map of { env_var: value } as an object.
##### Example:
`sh.env
FOO=bar
BAZ=bar
`
`sh.env.local
BAZ=qux
`
`js
const dotenvFlow = require('@codexsoft/dotenv-flow');
const variables = dotenvFlow.parse([
'/path/to/project/.env',
'/path/to/project/.env.local'
]);
console.log(typeof variables, variables); // > object { FOO: 'bar', BAZ: 'qux' }
`
#### .load(filenames, [options]) => object
Loads variables defined in a given file(s) into process.env.
When several filenames are given, parsed environment variables are merged using the "overwrite" strategy since it utilizes .parse() for doing this.
But eventually, assigning the parsed environment variables to process.env is done using the "append" strategy, thus giving a higher priority to the environment variables predefined by the shell.
##### Parameters:
##### filenamesstring|string[]
###### Type:
A filename or a list of filenames to load.
##### [options.encoding]string
###### Type: "utf8"
###### Default:
An optional encoding for reading files.
##### [options.options_load_env_local_in_test_env]boolean
###### Type: false
###### Default:
Enable loading load .env.local in test environment
##### [options.silent]boolean
###### Type: false
###### Default:
Suppress all the console outputs except errors and deprecation warnings.
##### Returns:
###### Type: object
The returning object have the same shape as the .config()'s, it will contain the parsed property with a parsed content of a given file(s) or the error property if the parsing is failed.
##### Example:
`sh.env
FOO=bar
BAZ=bar
`
`sh.env.local
BAZ=qux
`
`js
const dotenvFlow = require('@codexsoft/dotenv-flow');
process.env.BAZ = 'Yay!';
const result = dotenvFlow.load([
'/path/to/project/.env',
'/path/to/project/.env.local'
]);
console.log(typeof result, result); // > object { parsed: { FOO: 'bar', BAZ: 'qux' } }
console.log(process.env.FOO); // > 'bar'
console.log(process.env.BAZ); // > 'Yay!'
`
#### .unload(filenames, [options]) => void
Unloads variables defined in a given file(s) from process.env.
The environment variables that are predefined (i.e. by the shell) will not be unloaded.
##### Parameters:
##### filenamesstring|string[]
###### Type:
A filename or a list of filenames to unload.
##### [options.encoding]string
###### Type: "utf8"
###### Default:
An optional encoding for reading files.
##### Example:
`sh.env
FOO=bar
BAZ=bar
`
`sh.env.local
BAZ=qux
`
`js
const dotenvFlow = require('@codexsoft/dotenv-flow');
process.env.BAZ = 'Yay!';
dotenvFlow.load([
'/path/to/project/.env',
'/path/to/project/.env.local'
]);
console.log(process.env.FOO); // > 'bar'
console.log(process.env.BAZ); // > 'Yay!'
dotenvFlow.unload([
'/path/to/project/.env',
'/path/to/project/.env.local'
]);
console.log(process.env.FOO); // > undefined
console.log(process.env.BAZ); // > 'Yay!'
`
---
* dotenv-flow – original package
* dotenv-flow-webpack – a webpack plugin for using dotenv-flow in web applicationsdotenv-flow-cli
* – CLI executable that preloads environment variables using dotenv-flowdotenv-expand
* – environment variables expansion _(originally designed for dotenv, but also compatible with dotenv-flow)_
Feel free to dive in! Open an issue or submit PRs.
Using NPM:
`sh`
$ npm test
Using Yarn:
`sh`
$ yarn test
Licensed under MIT © 2022 Dmitriy Kozubskiy
Original package dotenv-flow` is licensed under MIT © 2018-2020 Dan Kerimdzhanov