I work on many projects that run on Express servers, whether they are front-end (i.e. React.js) codebases or server-side Node.js codebases.
Many times with the front-end codebases I would load conditional configuration based on NODE_ENV, such as the URL of the restful API that the front-end makes requests to.
I many times also used NODE_ENV to conditionally load things like DB configuration for server-side Node.js projects.
On a project that consisted of development, staging, and production (3 environments), I would usually set up my code to load configuration based on the NODE_ENV being set to any one of those 3 environments (and maybe also "local").
I was recently working on a project that was referring to the production environment as "live."
When I decided to set the NODE_ENV=live for this environment, a coworker pointed out a major flaw with this approach.
It seems that Express and some other libraries for Node.js latch onto the fact that you will either be using "production" or "development" as your NODE_ENV and using other names for your environments can have unexpected effects.
For example, Express needs NODE_ENV=production in order to run in "production" mode. According to the Express docs "Tests indicate that just doing this can improve app performance by a factor of three!"
Basically, I'm curious if it is considered common practice to set the NODE_ENV to values other than "development" and "production," like I've been doing in my projects.
I feel that if I'm going to deploy my code to the development or staging environments on the cloud, I don't think they should run in a different Express "mode" than the production environment.
Does it make more sense to maintain configurations separate from NODE_ENV?
For example, does it make sense to base your configuration off of a variable like APP_ENV, while ensuring that NODE_ENV is either "development" or "production" for frameworks/packages like Express.
NODE_ENV should be set to either development or production in traditional sense.
The reason being, when you're building a front-end application (React, etc), you either build the application in development mode or production mode. For example, in development mode, you will watch for changes and build continuously. In production mode, you minify the code and optimize it for size.
In case of a node server, the NODE_ENV refers to what mode you start your application with. For example, in development mode, you configure your server and install all devDependencies and watch for changes and live reload the server. And in production mode, you only install dependencies and start the server with optimized configuration.
Now talking about different production environments, say staging, pre-live, live, etc, you should use a separate ENV variable for this. Except local, all other environments are considered production environments and your app should be built with and start in production mode in these environments.
You usually load different configurations, say api keys, urls for each environment. These should be differentiated with a separate ENV variable like APP_ENV.
I usually use APP_ENV to differentiate between staging and live environments.
This is how the package.json will look like with different start scripts for different environments
"scripts": {
"start:local": "NODE_ENV=development APP_ENV=local your-start-script",
"start:staging": "NODE_ENV=production APP_ENV=staging your-start-script",
"start:live": "NODE_ENV=production APP_ENV=live your-start-script",
}
You will need to start the app with the right start script in each environment.
NODE_ENV is used to differentiate between development and production instances. It is not a good idea to run production code without NODE_ENV=production. NODE_ENV=development is usually not that important, because libraries usually just check to see if NODE_ENV !== 'production'. So if you want to have multiple production node environments, or production-like environments, each of them should set NODE_ENV=production. That said, you can definitely set other environment variables to whatever values you desire, and read them back from node at runtime.
A reasonable example would be to have a local staging and production versions of your configuration. In this case, I would recommend having NODE_ENV be just one of the parameters you set up for each environment. For instance, you might want three different databases for each of local, staging and production but set NODE_ENV to development on local, and production for both staging and production.
Since the variables will be shell variables, you will need a way of loading certain environment variables on the target operating system prior to running the server. Modules like https://www.npmjs.com/package/dotenv look promising for this purpose.
You can use NODE_ENV for multiple environments when your install the custom-env module:
npm install custom-env
Full docs here: https://www.npmjs.com/package/custom-env
Related
I would like to have a way to toggle between production and dev endpoints within from the phone settings. I am worried that this will mess up the cash and might display incorrect data. what's the best way to do it, please?
I was wondering the same thing!
In your code, you can use the __DEV__ global flag to differentiate between Dev and Production "mode".
Expo's Production switch is not a reliable way to handle environment switches (production and dev endpoints in your case). Why? 2 main reasons:
Production mode always minifies your code and better represents the performance your app will have on end-user's devices.
Development mode includes useful warnings and gives you access to debugging tools.
What if you want to have the flexibility to run the app against the Production endpoints you have, but still being able to access the debugging tools? You can't.
Here's my approach: I handle environment switches with .env files.
With Expo, I got my .env to work with the following:
Added babel-plugin-inline-dotenv to devDependencies:
npm install --save-dev babel-plugin-inline-dotenv
Added inline-dotenv to .babelrc:
{
"plugins": ["inline-dotenv"]
}
Added a .env file:
ENDPOINT="https://development"
Kudos for the .env set-up instructions goes to jdrydn.
Finally, use the environment variable in your code:
<Text>{process.env.ENDPOINT}</Text>
Plus, I have one more file .env-production (technically, I have also .env-staging in case you're wondering):
ENDPOINT="https://production"
The real caveat is when you want to run your app against the Production environment. You need to:
Copy .env-production content to the .env file.
Restart Expo's Metro Bundler and clear its cache (must be always restarted between .env changes). Do that either by running expo r -c or by pressing shift-r in your terminal to restart and clear cache while the Metro Bundler is running.
That's the most optimal approach I've been able to find.
PS: If you want to toggle between Dev and Production endpoints within you app - I'd simply use a js file with exported variables for each environment.
I'm familiar with gulp and the ability to have a distinct configuration for each environment within a single configuration file. That way running gulp dev would allow you to start your server with the configuration of your dev environment, gulp staging for your staging and gulp prod fro production.
I'm now looking at restify and am trying to determine if something similar can be done with it. I've tried researching this online and haven't found anything meaningful. Is this possible and if so could somebody provide an example?
You can use dotenv package to load different configuration file. For example
.env.dev For Development environment
.env.prod for Production environment
.env.test for Testing environment
you can import file according to NODE_ENV var
or you can simply add all configuration variable in one file for example
.conf.env and import it.
We're trying to run a ReactJs in production using Webpack as my build tool. For this purpose we use DefinePlugin to set environment variables.
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
'process.env.API_URL': JSON.stringify("http://localhost:7852/"),
}),
However for security reasons I don't want to have sensible information in my source code, as example we don't want to put api keys or private urls in webpack configuration. So we are planning to store that values in the environment variables of the production server.
We do the Webpack build in a continuous integration server (Docker Hub). We want to compile in the Docker Servers but we don't want to put the settings in the Docker servers, we only want the settings in the production server. However when we set these values in my production server the result is undefined. Is there a way to set some configuration variables in Webpack to be handle during transpilation (there are several methods) and let the the production server handle the others? Any advice? Thanks in advance.
I've just run into the exact same problem. What I've found is at the time when webpack is run, is where the environment needs to be set in order to be visible by the application. This means that only what's on your CI server (which builds a docker image) will be available to webpack and consequently your app.
Unless your app routes requests through it's server, where the environment is available, to an api server, I couldn't think of a decent solution. What I've chosen to do is set the environment during my TeamCity build and have a separate build for each test and production servers which would create a separate docker image but using the same dockerfile.
You can use different webpacks for different environments, and move the sensitive configuration to environment variables. Your app can only see environment variables in a container if they are defined in the Dockerfile using ENV. If you change your app to read all sensitive information from environment variables, and have matching ENV instructions in your Dockerfile, you can specify them at runtime when you start the container.
E.g. in webpack:
new webpack.DefinePlugin({
'process.env.API_KEY': JSON.stringify(process.env.API_KEY || 'API_KEY environment variable not defined'),
...
In your Dockerfile add ENV API_KEY and when you run your container, you can pass the environment variables with -e: docker run -e API_KEY=ba3d4db....
Better still, if you have multiple sensitive values, define them in the Dockerfile and at run time pass them in an environment file using --env-file:
> cat Dockerfile
FROM ubuntu
ENV API_KEY \
ANOTHER_SECRET
> cat env.file
API_KEY=ab3da3bda4d4a4c4c4
ANOTHER_SECRET=shhh!
> docker run --env-file env.file -it temp bash
root#545d5945ab1b:/# echo $API_KEY
ab3da3bda4d4a4c4c4
root#545d5945ab1b:/# echo $ANOTHER_SECRET
shhh!
Then you can have the same Docker config for every environment, with different contents in env.file, and secure access to the file.
My NodeJS project requires multiple environments. E.g. SIT/QA/PROD. I have set up the config files (e.g. qa.js) which specify which port to use and which db etc. My start script sets NODE_ENV according to the environment, and then i launch it by doing "npm start".
This works fine for a single environment, it starts up on the port it should and everything is fine. However, when I then start another environment, the first stops working. I assume this either do with the fact that NODE_ENV has changed, or is it something else? Does NODE_ENV only matter when you first run npm start or after too?
Can anyone please advise how to have multiple environments simultaneously running?
An environment is (almost) nothing special to Express. You can use it as a selection mechanism to set a certain configuration based on the value of NODE_ENV, like you're doing now.
Conceptually, you should think of it like this:
if (NODE_ENV === 'qa') {
// set configuration for qa
} else if (NODE_ENV === 'production') {
// set configuration for production
} else
// set configuration for development/testing/...
}
As you can see, this implies that you can only use one environment at a time. If you set the production environment, it will use the configuration for that environment, and nothing else.
Running multiple environments simultanously in a single process defeats the whole purpose of NODE_ENV. However, you can—to a degree—run multiple different versions of your app, each one running in a separate environment (below assumes that you're using some sort of Unix environment):
// start qa version
$ env NODE_ENV=qa npm start
// in another window, start production version
$ env NODE_ENV=production npm start
I say "to a degree", because it's not possible to have two different processes listening on the same TCP port. So your environments need to take that into account (for instance, have the QA instance listen on port 3000 and the production instance on 3001).
I'm using Meteor with less package, and trying to make a variable different on each environment. For example:
// on Dev
#staticPath: '/'
// on Production
#staticPath: 'http://s.mydomain.com/'
Any idea?
Thanks.
It is a bit difficult to do this comparing the effort you have to put in to compared with what you get.
You can create a package in your project that is a 'dev mode' package and another thats a production mode package
meteor create --package debugmode
meteor create --package productionmode
In the packages directory you should have two packages, in the debugmode package you can edit the package.js file and add debugOnly: true into the Package.describe method.
You can also add api.use("debugmode", "less") to the Package.onUse(function(api) { section of it. This ensures that if productionmode has a variable, then debugmode will override it.
You'll also need an api.use("less") in the productionmode package since its dependent on less.
In both packages you will have to use api.add_files to add any less files that are production mode or debug mode. Keep in mind debug mode overrides production mode so the variable names need to match. If you declare a production mode variable it will have to be nullified by the debug mode package.
Lastly add the packages in with meteor add productionmode debugmode
I've never personally tried this, but a variation of it in some way (perhaps including all the less files) in the two packages should work.