React environment variable not working in dev - node.js

Please put me out of my misery. I see scores of other people had the same issue and I don't see a solution.
I am trying to put my sensitive keys into environment-specific files (.env.development, .env.staging, etc). The keys work fine if I put them in .env but I need this file for some other items which must be pushed up to source control. All of the files are in root (I see that this was a common mistake). Is there something with webpack that is the issue? I have restarted the server instance every time I make a change.
const express = require('express');
const path = require('path');
const app = express();
require('dotenv').config();
console.log('ENV', process.env.NODE_ENV); // this returns "development"
console.log('Hello?', process.env.REACT_APP_HELLO); // this returns "undefined"
As noted I am surfacing the environment correctly.
"start": "SET NODE_ENV=development&& node server/index.js",
from package.json
REACT_APP_HELLO=BLAH
from .env.development

I assume you're in cmd.exe because of the set. Add a space before the &&: "start": "set NODE_ENV=development && node server", (no need to specify index.js. On non-Windows systems this would be NODE_ENV=development node server.
EDIT:
To get .env.development working, change the dotenv line to this: require('dotenv').config({ path: '.env.' + process.env.NODE_ENV }). (source), or the custom-env package: require('custom-env').env(process.env.NODE_ENV). Neither of those inherit from the regular .env though, so if you need that, check out dotenv-flow. I haven't tried the last package, but it seems to have the most features and the least amount of config to get working.

Related

NodeJS not recognizing .env file

I have like 5 NodeJS services running, but I have a problem in one of those.
This is the nodemon.json file:
{
"watch": ["**/*.ts"],
"ext": "ts,json",
"ignore": ["./test/*.ts"],
"exec": "node -r ts-node/register -r dotenv/config Index.ts dotenv_config_path=$(pwd)/.env",
"env": {
"NODE_ENV": "development"
}
}
It's the same as the rest of services. When I run npm run dev I got error messages depending on which value is taking from the .env file, example:
const LOCAL_CONFIGURATION = {
PORT_APP: 8082,
MONGODB: {
SERVER: process.env.MONGO_DTE,
AUTH: {
auth: {
password:process.env.MONGO_PASSWORD,
user:process.env.MONGO_USER
}
},
},
MS_NOTIFICACION: "http://localhost:8089/notificacion",
ELASTIC_PATH: process.env.ELASTIC_PATH,
...COMMON,
};
The first error message is:
ConfigurationError: Missing node(s) option
That message is produced because it's not reading the value from process.env.ELASTIC_PATH, but if I put a hardcoed value like "http://with.the.correct.url" and it tries again to run, I get another error:
Error: Credentials must be provided when creating a service client
That error is because it's trying to read password:process.env.MONGO_PASSWORD and user:process.env.MONGO_USER
etc, so, there's a problem on reading the .env file. I know that .env file has those values, and the file is in UTF-8, without quotes, etc. The .env file is the same file as the other services, it works ok in the rest but I don't know why is not getting read here.
Any idea?
EDIT:
Plus, I put a console.log(process.env); in config.ts file and it shows values like this:
But there's no values from the .env for example, there in the picture there's a value called COMPUTERNAME so if I put console.log(process.env.COMPUTERNAME); I get: IBM-NOT87
Why is not getting the .env file?
Seems like you need to require/configure dotenv. Docs:
As early as possible in your application, require and configure dotenv.
require('dotenv').config()
To further expand on #JBallin answer
you should use this on your app.js
Or if that does not work then you will need to explicitly add it to the file you are wanting to use those Variables
Sharing image, as its sometimes easier to see expanded
code here =>
require('dotenv/config') // require the dotenv/config at beginning of file
const express = require('express')
const mongoose = require('mongoose')
require('dotenv').config({ path: "./sample.env" });
In the file you are using environment variables,
As early as possible, require the "dotenv" and in the config() method, specify the path of the .env file, even if it in your root directory or the same directory where node starts.
The code for requiring and specifying file in the same directory is in the first line in the answer.
Also, for further reading 📖 , you can visit https://github.com/motdotla/dotenv#path
You cat try this.
-> npm i dotenv
and in code add this piece of code
require('dotenv').config({
path: 'your path here'
})
Install dotenv package
npm install --s dotenv
And add this require("dotenv").config(); in index.js/ts file.

process.env.NODE_ENV always 'development' when building nestjs app with nrwl nx

My NX application's npm run build:server calls ng build api-server that triggers the #nrwl/node:build builder.
It builds the NestJS application as main.js. Things work except I wanted process.env.NODE_ENV to be evaluated at runtime but I think it was resolved at build time (via Webpack).
Currently, the value is always set to 'development'.
I am new to Nrwl's NX. Any solution this?
In NestJs/Nodejs app in Nx.Dev workspace process.env.NODE_ENV is replaced during compilation from typescript to javascript very "smart way" to "development" string constant (everything like NODE_ENV is replaced). I don't know why. But only way how can I get real NODE_ENV in runtime is this code:
//process.env.NODE_ENV
process.env['NODE' + '_ENV']
The reason you're seeing development is because you're building the app in development mode - it's not best practice to evaluate at runtime because then the builder can't do fancy things to make the build production ready. If you want production, you need to build the app in production mode by adding the --prod flag (just like how you need to build Angular in production mode).
If you need to serve the app in production mode (instead of build) the default config doesn't provide you with a prod mode for serve. You'll need to add the configuration to your angular.json.
So this code:
"serve": {
"builder": "#nrwl/node:execute",
"options": {
"buildTarget": "api-server:build"
}
},
Would become
"serve": {
"builder": "#nrwl/node:execute",
"options": {
"buildTarget": "api-server:build"
},
"configurations": {
"production": {
"buildTarget": "api-server:build:production"
}
}
},
and then you can run
ng serve --project=api-server --prod
Indeed the nx builder will replace the expression process.env.NODE_ENV in our source code with the current value of the env var (or the nx-mode).
What happens is this:
the build command executes the nx builder which creates a configuration for web-pack
this configuration instructions for the webpack-define plugin to replace the text process.env.NODE_ENV during compilation with the actual value of the env-var (or the nx-mode):
see nx-code getClientEnvironment()
Since the webpack-define plugin will look for the text process.env.NODE_ENV, it's easy to use a workaround as explained in this answer:
process.env['NODE'+'_ENV']
Warning
When you need to apply this workaround to make your app work, then something is wrong. Since you have compiled your app in production mode, it does not make sense to pass another value for NODE_ENV when you start the (production) app.
The webpack Production page contains some helpful info.
We also had this case, and the issue was, that we relied on the NODE_ENV variable to load different database configs for dev, prod, test, etc.
The solution for our case was to simply use separate env-vars for the database config (e.g. DB_NAME, DB_PORT, ..), so that we can use different db-configs at runtime with any build-variants: dev, prod, test, etc.
I recently faced the same problem using Express instead of Nest.
What we did to overcome this was adding some file replacements when compiling for any of our environments (development, production, staging, staging-dev). This is done in the angular.json file the same way the environment files are replaced for the Angular app .
Another approach that worked for us, was loading the environment variables only once, and retrieve them from that origin. As our app relies on Express for it's backend the used the Express env variable as:
import express from 'express';
const _app = express();
const _env = _app.get('env');
console.log(_env); // shows the right environment value set on NODE_ENV
To come to this conclusion we checked Express code for the env variable and it does use process.env.NODE_ENV internally.
Hope it helps. Best regards.
We had the same issue, we eventually used the cross-env package in our package.json:
"prodBuild": "cross-env NODE_ENV=production nx run api-server:build:production",
"prodServe": "cross-env NODE_ENV=production nx run api-server:serve:production"
process.env is indeed only available at run-time. What is probably happening is that you are not setting this value when running your application. Can I ask how you are running it?
As a trivial example
# The following will read the environment variables that are defined in your shell (run `printenv` to see what those are)
> node main.js
# this will have your variable set
> NODE_ENV=production node main.js
Of course you want to have it actually set in your environment when deploying the app rather then passing it in this way, but if you're doing it locally you can do it like this.

How Do I Build For A UAT Environment Using React?

According to the React docs you can have development, test and production envs.
The value of NODE_ENV is set automatically to development (when using npm start), test (when using npm test) or production (when using npm build). Thus, from the point of view of create-react-app, there are only three environments.
I need to change root rest api urls based on how I am deployed.
e.g.
development: baseURL = 'http://localhost:3004';
test: baseURL = 'http://localhost:8080';
uat: baseURL = 'http://uat.api.azure.com:8080';
production: baseURL = 'http://my.cool.api.com';
How do I configure a UAT environment for react if it only caters for dev, test and prod?
What would my javascript, package.json and build commands look like to switch these values automatically?
Like John Ruddell wrote in the comments, we should still use NODE_ENV=production in a staging environment to keep it as close as prod as possible. But that doesn't help with our problem here.
The reason why NODE_ENV can't be used reliably is that most Node modules use NODE_ENV to adjust and optimize with sane defaults, like Express, React, Next, etc. Next even completely changes its features depending on the commonly used values development, test and production.
So the solution is to create our own variable, and how to do that depends on the project we're working on.
Additional environments with Create React App (CRA)
The documentation says:
Note: You must create custom environment variables beginning with REACT_APP_. Any other variables except NODE_ENV will be ignored to avoid accidentally exposing a private key on the machine that could have the same name.
It was discussed in an issue where Ian Schmitz says:
Instead you can create your own variable like REACT_APP_SERVER_URL which can have default values in dev and prod through the .env file if you'd like, then simply set that environment variable when building your app for staging like REACT_APP_SERVER_URL=... npm run build.
A common package that I use is cross-env so that anyone can run our npm scripts on any platform.
"scripts": {
"build:uat": "cross-env REACT_APP_SERVER_URL='http://uat.api.azure.com:8080' npm run build"
Any other JS project
If we're not bound to CRA, or have ejected, we can easily configure any number of environment configurations we'd like in a similar fashion.
Personally, I like dotenv-extended which offers validation for required variables and default values.
Similarly, in the package.json file:
"scripts": {
"build:uat": "cross-env APP_ENV=UAT npm run build"
Then, in an entry point node script (one of the first script loaded, e.g. required in a babel config):
const dotEnv = require('dotenv-extended');
// Import environment values from a .env.* file
const envFile = dotEnv.load({
path: `.env.${process.env.APP_ENV || 'local'}`,
defaults: 'build/env/.env.defaults',
schema: 'build/env/.env.schema',
errorOnMissing: true,
silent: false,
});
Then, as an example, a babel configuration file could use these like this:
const env = require('./build/env');
module.exports = {
plugins: [
['transform-define', env],
],
};
Runtime configuration
John Ruddell also mentioned that one can detect at runtime the domain the app is running off of.
function getApiUrl() {
const { href } = window.location;
// UAT
if (href.indexOf('https://my-uat-env.example.com') !== -1) {
return 'http://uat.api.azure.com:8080';
}
// PROD
if (href.indexOf('https://example.com') !== -1) {
return 'http://my.cool.api.com';
}
// Defaults to local
return 'http://localhost:3004';
}
This is quick and simple, works without changing the build/CI/CD pipeline at all. Though it has some downsides:
All the configuration is "leaked" in the final build,
It won't benefit from dead-code removal at minification time when using something like babel-plugin-transform-define or Webpack's DefinePlugin resulting in a slightly bigger file size.
Won't be available at compile time.
Trickier if using Server-Side Rendering (though not impossible)
To have multiple environments in a React.js application you can use this plugin
env-cmd from NPM
And after that Create the three files as per your need.
For example if you want to setup dev, stag and prod environments you can write your commands like this.
"start:dev": "env-cmd -f dev.env npm start", // dev env
"build:beta": "env-cmd -f stag.env npm run build", // beta env
"build": "react-scripts build", // prod env using .env file

Toggle between multiple .env files like .env.development with node.js

I want to use separate .env files for each mode (development, production, etc...). When working on my vue.js projects, I can use files like .env.development or .env.production to get different values for the same env key. (example: in .env.development: FOO=BAR and in .env.production: FOO=BAZ, in development mode process.env.FOO would be BAR, in production i'd be BAZ).
I'm working on an Express server and want to use these same kinds of .env files to store the port, db uri, user, pwd...
I know I can edit the scripts in package.json like this:
"scripts": {
"start": "NODE_ENV=development PORT=80 node ./bin/www",
"start-prod": "NODE_ENV=production PORT=81 node ./bin/www"
}
but this gets messy when using multiple variables.
I've tried using dotenv but it seems like you can only use the .env file. Not .env.development and .env.production.
Can I use the dotenv package or do I need another one? Or could I do this without any package at all?
You can specify which .env file path to use via the path option with something like this:
require('dotenv').config({ path: `.env.${process.env.NODE_ENV}` })
The above two answers are both slightly off. let me explain. Answer from KyleMit below is missing ./ to indicate current directory.
the ./ indicates current directory in Node.js. The Second . prior to env indicates a hidden/private file. Thats why in KyleMits answer if the env file was not in the root of the PC it would never find it. The simplest way to do it in my opinion is to add a ./
this says "hey computer look in my current directory for this file".
//look in my current directory for this hidden file.
// List hidden files in dir: ls -a
require('dotenv').config({ path: `./.env.${process.env.NODE_ENV}` })
//I reccomend doing a console.log as well to make sure the names match*
console.log(`./.env.${process.env.NODE_ENV}`)
I'm using the custom-env npm package to handle multiple .env files. Just put this at the top of your code:
require('custom-env').env();
and it will load environment variables from the file .env.X, where X is the value of you NODE_ENV environment variable. For example: .env.test or .env.production.
Here is a nice tutorial on how to use the package.
From answers from above:
This is the final result that worked for me.
.env.dev file in src dir.
import path from 'path';
dotenv.config({ path: path.join(__dirname, `./.env.${process.env.NODE_ENV}`)});
For Typescript:
import dotenv from 'dotenv'
dotenv.config({ path: `.env.${process.env.NODE_ENV}` })
Install the dotenv package:
npm install dotenv
We need to get the .env file name based on NODE_ENV, it might be undefined, set default to development:
const envFileName = `.env.${process.env.NODE_ENV || "development"}`
Import the dotenv package and use it:
dotenv.config({ path: envFileName });
The official doc of dotenv does not recommend having multiple .env files.
"Should I have multiple .env files?
No. We strongly recommend against having a "main" .env file and an
"environment" .env file like .env.test. Your config should vary
between deploys, and you should not be sharing values between
environments."

How can I set properties conditionally in Node.js via Atom?

I am developing a Node.js app with Electron via Atom.
I want to set some properties conditionally(or automatically), for instance, the url should be http://some.url on production level.
Currently, I use like this.
// win.loadURL('http://app.url/webchat'); //uncomment when production
win.loadURL('http://test.app.url/webchat'); // uncomment when development
This is very annoying me, and it can be a problem when I miss changing comments.
How can I change my properties conditionally with production/development level?
I have a config directory with different config files for different environments: dev, test, prod. Then in my package.json I have added environment specific build commands. e.g. For prod:
"build-prod-config": "config/buildProdConfig.sh",
"build-renderer-prod": "webpack --config=webpack.config.renderer.prod.js",
"build-main-prod": "webpack --config=webpack.config.main.prod.js",
"build-prod": "npm run build-prod-config && npm run build-main-prod & npm run build-renderer-prod",
buildProdConfig.sh
#!/usr/bin/env bash
cp config/app.config.prod.js config/app.config.js
echo "Copied ProdConfig to Config"
//This is what a config file looks like
const Config = {
suppDataDirectoryPath: '/suppData/',
builtFor: 'prod',
}
module.exports = Config;
I then require Config whereever I need in my application and use the values. This is a simple thing for now, but it works.
if (process.env.DEV === "PROD") {
win.loadURL('http://app.url/webchat');
} else {
win.loadURL('http://test.app.url/webchat');
}
then when launching your app just do
DEV="PROD" node app.js or whatever

Resources