I have a project handled by Webpack and Gulp. All the API's use a singular ROOT_URL variable and append strings to fetch information from the backend.
For multiple deployments I have different back end servers serving from different databases. Currently for each update I have to change the ROOT_URL and bundle the project in each individual server.
Ideally I would like to just pass a bundle to each deployed server so that each one will reference a local JS file containing their respective ROOT_URLs. Is there a way to do this and/or is there an industry standard?
Anytime you have a something that varies by server generally you would use environmental variables. If you need it in your javascript spa's you will have to pass the environmental variable from your server to your javascript(possibly through a templating engine like ejs if your using node). Ultimately how to do this depends on what backend technologies your using.
With Webpack you can specify the config file location. I use this to have separate dev and production configs. You could use this to have a separate config file for each environment that handles updating the ROOT_URL for you.
"scripts": {
"dev": "webpack --config ./src/config/dev.config.js",
"prod": "webpack --config ./src/config/prod.config.js"
},
You can define environment variables using Webpack's DefinePlugin
Imagine you have a deployment script npm run deploy
In your webpack.config.js add a DefinePlugin
module.exports = {
entry: 'app.js',
plugins: [
new webpack.DefinePlugin({
'process.env.API_ROOT': JSON.stringify(process.env.API_ROOT)
})
]
}
Then in your code:
api.js
var url = process.env.API_ROOT + '/resource'
fetch(url).then(function(){
// do stuff
})
Then when you deploy you can define different API roots
API_ROOT=https://api1.com npm run deploy (will deploy code with API 1)
API_ROOT=https://api2.com npm run deploy (will deploy code with API 2)
.. and so forth. Webpack will automatically replace all the occurrences of process.env.API_ROOT in your code with the correct URL.
Related
I have recently switched from using .env file to a secrets management tool, Doppler, and is trying to inject the variables from Doppler into React, which is built by webpack.
However, simply by running
doppler run -- webpack serve --config webpack.dev.js
does not do the trick. What should I do if I want to inject env from Doppler into webpack to build my React app?
I found out about the EnvironmentPlugin in webpack,
When you run doppler run -- webpack serve --config webpack.dev.js, doppler injected the env variables into the runtime and we can pass them with EnvironmentPlugin by specifying the key name in the array and pass that into the plugin
const { EnvironmentPlugin } = require("webpack");
module.exports = {
plugins: [
new EnvironmentPlugin(["SAMPLE_KEY"]),
],
};
Another approach is to pass an object instead, and this approach allows for default values in case doppler has failed to provide the key
const { EnvironmentPlugin } = require("webpack");
module.exports = {
plugins: [
new EnvironmentPlugin({"SAMPLE_KEY":"abcde"}),
],
};
As long as the key specified in either the array or the object matches that in doppler, this should work.
referencing webpack docs here
I'll answer my own question from my comment...since I finally figured it out.
If you are coming from using a .env file in your project - I had a very large one with over 50 lines.
Running from a Unix based IDE You can mount the .env file:
doppler run --mount .env npm run build
This wont work on Windows VSCode, so if you are running your build from Windows based IDE command line (like me), you have to download the .env file
doppler secrets download --no-file --format env > .env
Best practice would be to delete this unencrypted file after processing!!
and using Docker for a CI build on a config branch you will want to run:
doppler run --token ${TOKEN NAME} --mount .env npm run build
To enable create react app to run in SSL mode, we need to set HTTPS=true in the package.json like this
"start": "HTTPS=true react-scripts start",
However, I would like this only in production and not when development happens locally. I have a .env file, and would like to use a flag REACT_APP_USE_SSL=true in production and then REACT_APP_USE_SSL=false locally.
My question is, how can I use this environment variable in package.json? Or is there another way to seamlessly switch between HTTP and HTTPS mode for development and production environment?
As mentioned in the comment by #kiranvj, (and also after the reading the doc myself), this is easier than the other complicated solutions I was trying to implement.
You can just set HTTPS=true or HTTPS=false in your .env file and create react app will pick it up.
Im tring to build my ReactApp using an environment variable system.
Following online guides, I've create two different files
.env.developtment and .env.production
using the syntax for variables:
REACT_APP_BASE_SERVER_URL=myapp/
During development (using npm start to start the server), development file is loaded perfectly and all variable are stored in process.env global.
Unfortunally after compiling it with webpack, process.env is empty.
Im compiling my code with:
./node_modules/.bin/webpack --config webpack.config.js --env.production --progress --env.NODE_ENV=production --colors
and in my webpack there is no process.env override (using DefinePlugin is the common mistake).
Following a similar question on this site, I've tried to put this command in the config to avoid any process override:
node: {process: false}
but with this process.env will be totally undefined instead of empty ( {} ).
Is it possible to use this kind of environment system or .env files are only supported with npm build?
Sending the env as an argument does not set the application environment. Said so, I considering you are doing it inside your config. What would set up few env values is the option mode as development or production, which is available on webpack4 (You do -p or -d for the previous versions).
I dont know why you said using webpack.DefinePlugin is a common mistake. I have been using it and it works very well.
In last case, if you are sure you process.env should not be empty I would suggest you to do the syntax process.env["expected_attr"].
I am doing server side rendering inside my react app. Inside of app I have a few pictures so i have loder for them inside webpack-config
{
test: /\.(gif|png|jpg)$/,
loader: 'file-loader?name=assets/img/[name].[hash].[ext]',
},
If I run my code this way
cross-env NODE_ENV=production webpack --optimize-minimize --config webpack.config.prod.js,
I get an error
Warning: Prop `src` did not match. Server: "assets/img/profilna.1b1788096b2a10afe508dff672e50072.jpg" Client: "/assets/img/profilna.1b1788096b2a10afe508dff672e50072.jpg"
but if I run it like this
cross-env NODE_ENV=production webpack --p --config webpack.config.prod.js,
everything is good and functions perfectly like I want.
Why is that?
As I read -p is equivalent of
webpack --optimize-minimize --define process.env.NODE_ENV="'production'"
Since I am already setting production enviroment I don't need --define
cross-env NODE_ENV=production
with this, you are setting Node process.env.NODE_ENV but is not "being passed" or used - while bundling - inside the app. Basically, you need to create global variables for the app and set NODE_ENV to what you need by webpack. And this is what...
--define process.env.NODE_ENV="'production'"
...does. It will use Webpack DefinePlugin to set global process.env.NODE_ENV to be used while bundling the app.
I know this sounds a little bit unclear, I struggled to understand it myself but hopefully, documentation will clear that out.
Technically, NODE_ENV is a system environment variable that Node.js
exposes into running scripts. It is used by convention to determine
dev-vs-prod behavior by server tools, build scripts, and client-side
libraries. Contrary to expectations, process.env.NODE_ENV is not set
to "production" within the build script
See "Specifying the environment" for an example.
My dev box is my local machine. The angularjs front-end makes api calls to the express backend and for this I store the api route and other details in a configuration file :
'use strict';
var env = process.env.NODE_ENV || 'development';
if (env === 'development') {
exports.serverPort = 3000;
exports.baseUrl = 'http://localhost';
exports.apiPath = 'http://localhost:3000/';
...//database path
.... // secret key etc
....
} else {
exports.serverPort = process.env.PORT;
exports.baseUrl = 'https://secret-ef-12.herokuapp.com';
exports.apiPath = 'https://secret-ef-12.herokuapp.com:' + process.env.PORT + '/';
....
}
my dev box has NODE_ENV set to 'development'.
On my heroku account, i have NODE_ENV set to 'production'.
Things work fine on dev machine. However on the heroku account (which is the production machine), the application fails to pickup production values.
please note that after deploying to Heroku I am rebuilding the code using my gulpfile.
//a snippet from package.json
"scripts": {
"start": "node server/server.js",
"postinstall": "node node_modules/.bin/gulp build"
},
To reiterate:
After 'git push heroku master', heroku rebuilds my application. No errors are reported.
The angularjs application opens fine when executing 'heroku open'. However all database calls that the application makes go to
http://localhost:3000
instead of using production values.
I do not understand why the angularJs application on the heroku platform does not pickup production values.
Eventually found the issue and solution. Browserify does not read node environment variables. Hence the code above would always return 'development'.
I configured my build file (Gulpfile.js) to generate a development or production build as per a flag passed to it.
What this meant was that my build file (Gulpfile.js) was not searching the environment to tell it whether it should build for dev or prod.
my gulp file is now configured as
gulp build --dev
or
gulp build --production
After deploying code to Heroku, i placed the following line in package.json
"postinstall": "node node_modules/.bin/gulp build --production"
For now this is working for me. It also keeps things simple which i prefer.