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

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.

Related

process.env.NODE_ENV suddenly UNDEFINED in current SvelteKit project

Inside svelte.config.js I was using this
const dev = process.env.NODE_ENV === 'development';
to conditionally set a base path which was working fine in projects with #sveltejs/kit#1.0.0-next.350 and *.357
After installing now the most recent SvelteKit version #sveltejs/kit#1.0.0-next.386 it only results to undefined
Differences I notice is that the new project lists "vite": "^3.0.0" as devDependency and the script changed from "dev": "svelte-kit dev", to "dev": "vite dev"
Update: It's also the case for a project with #sveltejs/kit#1.0.0-next.366, vite#2.9.14, "dev": "vite dev" - so the switch was before vite 3.0
Going through the vite docs I find import.meta.env, but that's also undefined inside svelte.config.js
Switching from Node v16 to 17 didn't make a difference as well
What changed and how can I now distinguish between dev and build mode?
The behavior you describe was introduced in 100-next384
[breaking] remove mode, prod and server from $app/env (#5602)
and here is the relevant discussion
respect --mode, and remove server, prod and mode from $app/env
I think you should use Vite capabilities to configure dev VS production/build modes.
Update
Considering better your case, a way to solve the issue is to set a value for the NODE_DEV environment variable, like (Linux/Mac):
export NODE_ENV=development && npm run dev
There are other ways to do this but at least for development it should do the trick.
This might help someone in the future. I encountered this error when I created an express server in a react application. I created the server directory in the root folder of the react application and I was running both react app and express server at the same time.

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

Node js express project how to add environment variables

I am a java script full stack developer.
In the vue js I create two files in the root.
env.development
env.production
In my main.js file, I can access environment variables like this
process.env.VUE_APP_MY_VARIABLE_X
How production and development differs in the vue js is,
if I use npm run serve project loaded the development environment variables. If I use npm run build it takes production environment variables. That's all in vue.
But in my expressjs project,
"start": "nodemon server.js --exec babel-node -e js",
This command will always responsible for the run project. I couldn't find two commands like in vue. I go though the tutorials every body says use the package called dotenv.
I couldn't figure out that how could this package identify the environments this is production and this is development.
End of the day I want to set my db password to 123456 in my local machine and root#123456 in the server. How could I achieve this?
Vue.js handles modes automatically, depending on the command (e.g. serve sets NODE_ENV=development, while build sets NODE_ENV=production).
Depending on the mode, it also automatically loads files with variables from disk.
Environment variables in Node.JS
However, Node.JS does not do that by default, and you have to set up your application to handle environment variables.
In Node.JS you can pass variables via the shell command, for example:
MY_VARIABLE=x node myapp.js (by prepending the command with variables), and that will give you process.env.MY_VARIABLE inside your myapp.js application.
Following this behavior, there's a consensus on using NODE_ENV variable for setting up the environment mode.
For production use, you'd start your application using NODE_ENV=production node myapp.js, while for development NODE_ENV=development node myapp.js.
Of course, if your machine already has these environment variables set up (for instance in .bash_profile) you do not need to prepend your command with them.
As a practice, in development machines, you'd have these variables already set up on your machine, while production machines (e.g. docker containers, etc) start clean and you pass the environment variables when starting your application.
For example, Heroku (and other deployment services) allow you to set up environment variables which are set at machine start.
There's also the method of storing variables in files (such as the .env, or other files), but those you'd have to read from disk when your application starts.
And this is where dotenv (and config package) come in play.
What dotenv does, is it reads .env file stored in your application execution path (root of the app), and sets any variables defined there into process.env for Node.JS to use.
This is extremely useful for development machines, but on production it's recommended to not have files that store sensitive information in variables, but rather use system environment variables.
There is also the option, for production machines, to construct or load into the machine an .env file at machine setup time, into the app directory.
Note:
never commit .env or config files that store passwords and sensitive information, to your repository
never store development and production variables in the same file
The dotenv approach
The best way to go about it, would be to use dotenv, and have different .env files, one on your development machine, and a different one on your production machine. That way, when your application starts, it reads the variables that are stored in the adjacent .env. This is the most secure and reliable way, in absence of means to pass environment variables to your production machine from a machine management application/interface.
The easiest way to set up your app with dotenv would be to set your start command to something like:
"start": "nodemon --exec \"node -r dotenv/config\" server.js"
and then in the .env file stored on your local/development machine you'd have something similar to:
DATABASE_PASSWORD=1235
DATABASE_USERNAME=joe-dev
...
and in the .env file on your production machine (server):
DATABASE_PASSWORD=root#1235
DATABASE_USERNAME=root
...
according to my understanding
you don't have created and setup a config file and required it in your app.js or any other file. let see for example create config file
const config = {
"development":{
"host":"localhost",
"dbport":"27017",
"port":"",
"username":"",
"password":"",
"userdb":"",
"passworddb":"123456",
"authSource":"",
"database":"devDB"
},
"staging":{
"host":"",
"dbport":"",
"port":"",
"username":"",
"password":"",
"passworddb":"#123456",
"database":"stageDB",
},
"production":{
"host":"",
"dbport":"",
"port":"",
"userdb":"",
"passworddb":"#123456",
"username":"",
"password":"",
"database":"prodDB",
}
};
module.exports = config;
then you have you setup a variable in your .env file like NodeENV = 'prod'
then import it into your files like
var config = require('./config')["process.env.NodeENV"];
and one more last thing dotenv is only used to loads environment variables from a .env file into process.env.

react-native unable to assign Production for NODE_ENV variable

I am trying to utilise the process.env.NODE_ENV to switch between the environments for my react native app, and am facing two issues here
1. Assigning parameter to NODE_ENV doesn't seem to change the env value straight forward,
executing the below command shows that it changed process.env.NODE_ENV value but doesnt seem to have changed the global.process.env.NODE_ENV
execution command:
NODE_ENV=development npm start -- --reset-cache
output: Please note, the second log statement still prints out development.
console.log("printing the changed value - ", process.env.NODE_ENV); // --> printing the changed value - production
console.log("checking the env content - ", process.env); // ---> checking the env content - { NODE_ENV: 'development' }
Workaround that i tried is to assign the process.env.NODE_ENV to the global instance,
global.process.env.NODE_ENV = process.env.NODE_ENV
Please do advise if this is a good approach to proceed with.
2. Assigning "Production" as a value to NODE_ENV throws Attempted to Assign to read only property error,
Execution Command:
NODE_ENV=production npm start -- --reset-cache
Please find image attached -
AssignToReadOnlyPropertyIssue
The problem your facing here comes from an erroneous assumption:
react-native doesn't execute javascript in the same way as node.
Let's consider the following npm script:
"scripts": {
"start": "node index.js"
}
When running SOME_VAR="some value" npm start, process.env will contain a entry for that environment variable, since it is running from the process where SOME_VAR was defined.
In react-native something different happens:
calling npm start starts the react-native packager which bundles your javascript code and then sends that javascript bundle to the installation in your emulator or device, where the bundle is in its turn executed. react-native's packager doesn't "forward" the environment variables defined where the javascript was bundled.
Hopefully there are packages that aid us to be able to keep using the same patterns we're used to using when developing node apps.
One I found really helpful was babel-plugin-transform-inline-environment-variables. This is a babel plugin that transforms process.env.SOME_VAR type calls to the value of SOME_VAR during bundle time.
The only headache I had with it was that when I was first trying to see how it worked I was changing the environment variables, but it only seemed to work "randomly". Turns out I was a victim of webpack's cache. If there are no changes in the code it is watching it won't update the bundle. I then learned I had to be aware of that

Node JS, detect production environment

When I'm in dev mode I don't want my server to send email and some other stuff so I have this in server.js file:
process.env.NODE_ENV = 'development';
When I move it to production i change the value to 'production'.
Problem is of course that it is easy to forget on deployments. Is it possible somehow to detect when server is in production?
If it's not, is it possible to write a batch script that replaces a string in a file?
You shouldn't manually change values of process.env object, because this object is reflecting your execution environment.
In your code you should do the following:
const environment = process.env.NODE_ENV || 'development';
And then you launch your app in production like this:
$ NODE_ENV=production node app.js
If NODE_ENV environment variable is not set, then it will be set to development by default.
You could also use dotenv module in development to specify all environment variables in a file.
Furthermore, I've implemented a reusable module, which allows to automatically detect environment by analyzing both CLI arguments and NODE_ENV variable. This could be useful on your development machine, because you can easily change environment by passing a CLI argument to you Node.js program like this: $ node app.js --prod. It's also nice to use with Gulp: $ gulp build --prod.
Please see more details and use cases on the detect-environment's page.
The suggested way to tackle these kind of problems is by using the process global object provided by NodeJS. For example:
var env = process.env.NODE_ENV || "development";
By the above line. We can easily switch between development / production environment. If we haven't configured any environment variable it works with development environment.
process.env is an object that contains the user environment.
For example if we are running our application in a bash shell and have configured NODE_ENV to production. Then i can access that in node application as process.env.NODE_ENV.
There are multiple ways to define this NODE_ENV variable for nodejs applications:
## Setting environment variable for bash shell
export NODE_ENV=production
## Defined while starting the application
NODE_ENV=production node app.js
So i don't think you would require any batch file for this. When handling multiple configuration variables, follow this.
This is normally handled through configuration files (e.g. config frameworks like node-convict) and through environmental variables. In the start command in production, you might do something like:
NODE_ENV=production npm start
then the process.env.NODE_ENV would be properly set by any module in your app that cares.

Resources