YahiaRefaiea
YahiaRefaiea

Reputation: 312

Migrate from npm package `cross-env` to `dotenv`

I've been reading about the .env file and decided to improve my workflow. Previously, I used cross-env to add env-variables right to the npm scripts. example:

"server": "babel-node server.js"
"server:prod": "cross-env NODE_ENV=production babel-node server.js"

And along with that, I had a file with global configs and helpers that contains:

let configs = {}
configs.env = process.env.NODE_ENV || 'development'
configs.devMode = function() {
  if(this.env === 'development') return true
}

// Usage:
const url = configs.devMode() ? 'devUrl' : 'prodUrl'

The dotenv documentation explained almost all my questions. I know I can create a .env file and pass variables like:

HOST=localhost
PORT=3000

But, for production, should I write NODE_ENV in the same file? Or is there another way to do it?

The change I'm expecting is having one npm script to run the server on both development and production. But, how is my .env file gonna looks like? And also how to improve my configs file with the new changes? Should I store the NODE_ENV in a variable and default it to development? Or NODE_ENV will decide the default elsewhere?

Thanks for your help. ^^

Upvotes: 2

Views: 1570

Answers (1)

D.K
D.K

Reputation: 583

This hasn't been commented on or answered for a long time; however, in case anyone is running into this similar problem of migrating from cross-env to dotenv, I will try to answer the OP's question here and provide a succinct guide on how to migrate to using dotenv instead of cross-env.

First of all, to answer the OP's question, the NODE_ENV environment variable can still be added to the NPM script in the package.json like this (make sure that you have both the dotenv and dotenv-cli packages installed before changing the script):

dotenv -e .env -v NODE_ENV=production babel-node server.js

Here, the -e argument supplies which .env files to use (you can supply multiple by having multiple -e arguments with the .env file names), and the -v argument supplies the environment variable for this specific run/script/command; with -e arguments, you only provide the file names whereas for -v arguments, you supply both the key and the value of this variable using KEY=VALUE syntax as you see in this example. For more details about the arguments regarding dotenv-cli, check the doc on the package: https://www.npmjs.com/package/dotenv-cli

To break the command down in this above example, I am telling NPM to run the babel-node command, executing the server.js, with the .env file as one of the environment files that would contain other environment variables (like your HOST and PORT variables) and with the NODE_ENV environment variable set to "production", all using the dotenv command to incorporate the supplied environment variables to the run time of the execution of this command. As long as you have the dotenv and dotenv-cli packages both installed as your project's dependencies, these environment variables supplied either by the .env file under the -e argument and the variables provided directly by the -v arguments should register to the current execution of the JavaScript.

On the OP's second question about whether to keep the NODE_ENV variable in the .env file or pass it as an argument, I will say that it will depend on the project, but generally, I will say it would be better if you have two separate scripts in your package.json that passes two different values for the development run and the production run by having the commands like this:

"scripts": {
  "server:dev": "dotenv -e .env -v NODE_ENV=development babel-node server.js",
  "server:prod": "dotenv -e .env -v NODE_ENV=production babel-node server.js"
}

With these, then you can supply different .env files for different environments on the fly by having two different .env files with the corresponding environment name attached to the file name, such as .env.development and .env.production.

And then, in your code, you would want to have this (also, simplifying the code posted by the OP as well as some of the codes wouldn't be necessary):

import * as dotenv from "dotenv";

export const getEnv = () => {
    dotenv.config({
        path: `.env.${process.env.NODE_ENV}`,
    });
};

This code snippet above will get the appropriate .env file based on whether the supplied NODE_ENV variable from one of the commands in the scripts section (as shown in the above example) if those environment files exist. For example, you had one environment file, .env.development, with the environment variables specific to your development environment and ran the server:dev script via npm run server:dev (which will run dotenv -e .env -v NODE_ENV=development babel-node server.js), the NPM will try to grab and inject the environment variables and their values from the .env.development file to the current run time of the current execution. The same can be said about running your program in the production environment. More on this here: https://www.npmjs.com/package/dotenv

With that in mind, to use the environment variables, you would just have this in your code:

// Usage
const url = `http://${process.env.HOST}:${process.env.PORT}`

Based on the environment variables found in the environment files you've supplied, it will use the appropriate HOST and PORT values; however, note that this would only work if you have two different environment files for two different environments, namely development and production environments, and have different HOST and PORT variable values, like this:

.env.development:

HOST=localhost
PORT=3000

.env.production:

HOST=https://some-actual-address.com
PORT=8080

With that being said, generally, you would have the .env file (without the environment name attached to it) for sensitive values like the API token that would be necessary for both environments (which you wouldn't usually push to the repo):

.env:

API_TOKEN=*************

A lot of projects I've worked with so far usually had maybe 2 or 3 environment files (.env files): one for the development environment, one for the production, and a common one for all environments. Note that any of these .env files shouldn't have any sensitive information/values if they are being pushed to the repo, as having any credentials or API token in the .env file in the repo would be a security risk.

I hope this helps.

Upvotes: 0

Related Questions