npm scripts: read .env file
Asked Answered
G

6

54

I have a simple requirement: In my npm scripts package.json file I have the line:

{
    "scripts": {
        "example": "some-lib --argument --domain \"https://tld.com\""
    }
}

Now I want the "domain" to be factored out.

First try is to use $npm_package_config, which works:

{
    "config": {
        "domain": "https://tld.com"
    },
    "scripts": {
        "example": "some-lib --argument --domain \"$npm_package_config_domain\""
    }
}

But I want the domain loaded from an local .env file.

I did not find any solution out there to read the contents of an env file inside npm scripts on the command line.

Can somebody give me a hint to a possible solution for this problem?

Gulfweed answered 1/1, 2019 at 20:18 Comment(1)
Have you seen dotenv-cli? npmjs.com/package/dotenv-cliStich
P
16

A much easier solution is to just use bash to parse the env file in-line and extract the variable you want:

Assuming your .env file looks like:

DOMAIN=https://tld.com
"scripts": {
  "example": "some-lib --argument --domain $(grep DOMAIN .env | cut -d '=' -f2)"
} 
Peso answered 21/9, 2019 at 9:4 Comment(4)
However, not so cool when cross-platform is a requirement as this fails in non-bash shells - notably windows cmd.exeReeducate
This is quite old yet still useful to me. Just wondering if there are any enhancements to this? Also is it possible to supply a default value for the env var, just in case it’s missing in the env file or the file doesn’t exist. I know I can do ${DOMAIN:-mydomain} but what is the syntax in this use case?Conah
This answer has some enhancements, such as ignoring lines beginning with #, handling values with spaces in them, and loading an entire .env file instead of just one variablePeso
cut: invalid field value ')' error in Windows Git Bash.Fordham
R
25

Short answer: There's no terse way to achieve this that works cross-platform, as per your second example which references the $npm_package_config variable.

For a cross-platform solution, i.e. one that works successfully on both *nix and Windows platforms - whereby the default shell utilized by npm scripts is either sh or cmd respectively, you'll need to execute your command (i.e. the one which is currently defined in your npm-script) via a nodejs helper script. Essentially, your nodejs script will need to:

The nodejs helper script can then be invoked via your npm-script.

The following describes how to achieve a solution that runs cross-platform.


Solution

1. The .env file

Firstly, lets assume we have a .env file residing in the root of our project directory. The .env file contains the following entry:

DOMAIN=https://tld.com

2. Install

The following nodejs script utilizes the dotenv package to load the environment variable(s) from the .env file. We'll need to install it. To do this cd to your project directory and run the following command:

npm i -D dotenv

3. The Node.js script (some-lib-cmd.js)

Next create a nodejs script as follows. Let's name the file some-lib-cmd.js and save it in the root of the project directory:

// Requirements...
require('dotenv').config();
const execSync = require('child_process').execSync;
const path = require("path");

/**
 * Creates a path to an executable in the node_modules/.bin directory. Each
 * path segment is joined with the appropriate platform-specific separator as
 * a delimiter.
 * @param {String} cmd The name of the executable.
 * @returns {String} The path to the executable.
 */
function getBinFile(cmd) {
  return path.join('node_modules', '.bin', cmd);
}

// Execute the command...
execSync(`${getBinFile('some-lib')} --argument --domain ${process.env.DOMAIN}`, { stdio: [0, 1, 2] });

Notes:

  • If your .env file does not reside in the root of our project directory along with some-lib-cmd.js, then you can utilize dotenv's path option to define a custom path to the location of your .env file instead. For example:

    require('dotenv').config({ path: 'path/to/another/folder/' })
    
  • To reference the DOMAIN variable from within the nodejs script we utilize process.env, i.e. process.env.DOMAIN.

4. package.json

In the scripts section of your package.json define the following script:

"scripts": {
  "example": "node some-lib-cmd"
}

Note: If you have chosen to save some-lib-cmd.js elsewhere, i.e. not in the in the root of your project directory, then redefine the path in your example script as necessary. For instance:

"scripts": {
  "example": "node path/to/some/folder/some-lib-cmd"
}
Reeducate answered 2/1, 2019 at 14:24 Comment(6)
Thanks RobC for your detailled help. I just published a package for this problem, have a look at github.com/vielhuber/from-envGulfweed
@DavidVielhuber github.com/toddbluhm/env-cmd has been doing this for awhile?Fer
@DavidVielhuber - You say "can you give an example how ..." Is this a question for me or Akkuma ? b.t.w Briefly looking at 'env-cmd' it doesn't seem to provide a bulltin solution to use a variable inside your npm script as Akkuma suggests.Reeducate
Sorry, wrong thread. @Fer can you give an example how env-cmd uses environment variables inside a npm script? I don't think thats possible.Gulfweed
"scripts": { "webhooks:dev-rg": "env-cmd --use-shell 'ts-node src/MyUtils/webhooks_register.ts https://${DEV_RESOURCEGROUP}.azurewebsites.net'"...Jollenta
any update since nodejs.org/dist/latest-v20.x/docs/api/cli.html#--env-fileconfig ?Sakhuja
P
22

Let's say you have a .env file like this:

DOMAIN="https://tld.com"

Then, you can use source in your command, like so:

{
    // ...
    "scripts": {
        "example": "source .env && some-lib --argument --domain $DOMAIN"
    }
}

This won't work in Windows by default, but if you have Git Bash installed, then you can configure npm to use it to run commands with e.g. (from here):

npm config set script-shell "C:\\Program Files\\git\\bin\\bash.exe"
Perlite answered 14/8, 2021 at 8:35 Comment(6)
I had to specify the path to the env file as ./.env or else I got a file not found error. As a side note, if you need to persist these variables into the node process subshell you can add set -a && before the source command to automatically export all the vars it contains.Champerty
Please mind that if you run this setup as part of an npm script, you have to specify path to .env file relative to package.json, not to the file in which your script is writtenUranian
Using source in a npm script returns sh: 1: source: not found. I'm on Ubuntu.Midstream
I believe source can be replaced by simply .. @Fred, try this :-)Perlite
Thanks for the hint! I'll try. Using env-cmd npm package right now and it works fine :-)Midstream
Is there a reason why this is being up-voted when it isn't cross platform? Isn't that the point of using node? Should support Windows and LinuxAvelin
P
16

A much easier solution is to just use bash to parse the env file in-line and extract the variable you want:

Assuming your .env file looks like:

DOMAIN=https://tld.com
"scripts": {
  "example": "some-lib --argument --domain $(grep DOMAIN .env | cut -d '=' -f2)"
} 
Peso answered 21/9, 2019 at 9:4 Comment(4)
However, not so cool when cross-platform is a requirement as this fails in non-bash shells - notably windows cmd.exeReeducate
This is quite old yet still useful to me. Just wondering if there are any enhancements to this? Also is it possible to supply a default value for the env var, just in case it’s missing in the env file or the file doesn’t exist. I know I can do ${DOMAIN:-mydomain} but what is the syntax in this use case?Conah
This answer has some enhancements, such as ignoring lines beginning with #, handling values with spaces in them, and loading an entire .env file instead of just one variablePeso
cut: invalid field value ')' error in Windows Git Bash.Fordham
P
8

There is a library that does exactly what you want to follow 12factor app best practices: the dotenv package from npmjs.org

https://www.npmjs.com/package/dotenv

FTA:

$ node -r dotenv/config your_script.js dotenv_config_path=/custom/path/to/your/env/vars
Penthouse answered 6/8, 2020 at 20:29 Comment(1)
Is this still required, even after nodejs.org/dist/latest-v20.x/docs/api/cli.html#--env-fileconfig ?Sakhuja
G
3

Surprised no one mentioned the 'ol tried and true DirEnv to auto setup environment for your current working directory. That way env is private to your local machine and available to all your scripts including inside the code base. It comes in handy for things that won't look at a .env file by default.

I use this all over my machine not just in development projects.

Def worth checking out! Direnv

Goldcrest answered 22/10, 2020 at 20:14 Comment(2)
Yeah this is cool but can't expect other to have it too.Conjunct
If others are installing your software, you can cause put your dependencies in package.json and then others will have the dotenv lib too....Keener
P
0

dotenv-cli has a great solution for this (as well as good explanation for the problem):

If your .env file looks like:

SAY_HI=hello!

you might expect dotenv echo "$SAY_HI" to display hello!. In fact, this is not what happens: your shell will first interpret your command before passing it to dotenv-cli, so if SAY_HI envvar is set to "", the command will be expanded into dotenv echo: that's why dotenv-cli cannot make the expansion you expect.

They give a workaround:

.env:

npm_package_config_domain=https://tld.com

package.json:

    "scripts": {
        "_example": "some-lib --argument --domain \"$npm_package_config_domain\"",
        "example": "dotenv -- npm run _example"
    }
Pernick answered 12/6 at 20:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.