Best way to manage env variables for multiple environments - reactjs

So, we have a React project. We have 3 branches: development, qa and staging. This is the code for the API URL in the 3 envs:
development:
const API_URL = process.env.NODE_ENV === 'development' ? 'https://our-website-development.com/api' : '/api';
qa:
const API_URL = process.env.NODE_ENV === 'development' ? 'https://our-website-qa.com/api' : '/api';
staging:
const API_URL = process.env.NODE_ENV === 'development' ? 'https://our-website-staging.com/api' : '/api';
Of course this has a problem: we have this conflict everytime we move things between environments.
So I want to move this to ENV variables.
But I have some doubts about how to implement it. I have some questions.
Option 1: have three .env files (.env.development, .env.qa, .env.staging) each one with the correct URL, push this file to the three branches, and then add scripts to start the project like npm start development or npm start qa.
Option 2. have only one .env file and don't push it to the project, make it static for every environment. This would mean having to manually change the endpoint url everytime I switch branches when developing.
Is there a better option?

Option 1 with some change will do the job. Keep 3 environment files and instead of using them in 3 branches, keep 3 npm tasks for start and build. While building ,keeping separate folders will be useful. Let me know if you need examples for this idea. I am using env-cmd to hook my .env files to my tasks.
Update:
Below is my scripts from Package.json, where I have 2 environments test and production,
"scripts": {
"start": "set PORT=41100 && env-cmd -f .env.test react-scripts start",
"build": "set \"GENERATE_SOURCEMAP=false\" && react-scripts build",
"build:test": "env-cmd -f .env.test npm run-script build && DEL /S /Q test && move build test",
"build:production": "env-cmd -f .env.production npm run-script build && DEL /S /Q production && move build production",
"test": "react-scripts test",
"format": "prettier --write src/**/*.ts{,x}",
"lint": "tsc --noEmit && eslint src/**/*.ts{,x}"
},
I am using Windows for development. If you are not this post may help to adjust your scripts,
Use custom build output folder when using create-react-app
Configurations flies are .env.test and .env.production and contents will look like,
REACT_APP_SOME_API = "https://myurl/api/resource"

Related

How to run a different build command for staging environment when publishing a react amplify application

I have an amplify react application with two environments so far: prod and staging.
Then I have my .env.staging and .env.production files with different values for an API URL.
Therefore, in my package.json I have the following scripts ready for the deployment:
"build": "react-scripts build",
"build:staging": "env-cmd -f .env.staging react-scripts build",
Now the problem comes as I don't know how to make amplify publish command to run one or the other depending on the environment.
No matter which amplify env checkout I choose, the configuration used on the 'publish' command is shared in the 'project-config.json', and it looks like the following:
{
"projectName": "whatever",
"version": "3.0",
"frontend": "javascript",
"javascript": {
"framework": "react",
"config": {
"SourceDir": "src",
"DistributionDir": "build",
"BuildCommand": "npm.cmd run-script build",
"StartCommand": "npm.cmd run-script start"
}
},
"providers": [
"awscloudformation"
]
}
Is there any way to achieve what I'm looking for?
Thanks for your help in advance.
I understand this question was asked almost 1 year ago now, but i encountered this same problem this morning and would like to offer my solution to the problem.
Currently (asof 04/03/22) there is still no official solution to the problem and as such, you will be required to edit your build script to build your content based on the environment dynamically.
The way we have currently implemented this is by creating a build JS script in our root (named build-env.script.js) containing the following code:
#! /usr/bin/env node
(function() {
// bring in child_process to use spawn command
const cp = require('child_process');
// bring in the amplify local-env-info.json to see current environment
const buildInfo = require('./amplify/.config/local-env-info.json');
// spawn the build command based on the env name:
// npm run build-production on prod or npm run build-staging on staging
const cmd = cp.spawn(`npm run build-${buildInfo.envName}`, { shell: true });
// echo output of the commands to the console
cmd.on('spawn', () => console.log('Running build command for:', buildInfo.envName));
cmd.stdout.on('data', (d) => console.log(d.toString()));
cmd.stderr.on('data', (d) => console.log(d.toString()));
cmd.on('exit', () => console.log('Build Completed'));
})();
As a sidenote, the JSON file in question is created by amplify and therefore can be determined as a source of truth when it comes to looking for the current environment name, my file for example looks like this:
{
"projectPath": "path/to/project/frontent",
"defaultEditor": "vscode",
"envName": "production"
}
Whilst my package.json looks like this:
{
...
"scripts": {
...
"build": "node ./build-env.script.js",
"build-staging": "ng build --configuration staging",
"build-production": "ng build --configuration production"
}
}
However you will need to modify the env names and scripts accordingly (as our project is an angular project.

Not able to fetch .env urls to my component on my react project

I am using 3 .env file like .env.prod, .env.dev and .env. But not able to fetch the url to my component.
I am using react 16.9.
Can you please help me why I am not able to fetch it?
in my .env / .env.dev files
loginUrl = = "http://localhost:8080/api/2.0/admin/auth/login"
in my package.json files
"scripts": {
"start": "cp ./.env.dev .env && react-scripts start",
"build:dev": "cp ./.env.dev .env && react-scripts build",
"build:stage": "cp ./.env.stage .env && react-scripts build",
"build:prod": "cp ./.env.prod .env && react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},```
Inside my component, when I am printing it it is giving undefined.
console.log(process.env.loginUrl) is giving undefined
Using react-script
Looking at react script adding-custom-environment-variables documentation, variables are automatically loaded from .env if:
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. Changing any environment variables will require you to restart the development server if it is running.
Seams like the problem come from the name of your variable, try renaming in REACT_APP_LOGIN_URL
Note: this feature is available with react-scripts#0.5.0 and higher.
If using Webpack instead of react-script
You need to use webpack DefinePlugin to inject environments variables in your code.
In your webpack.config.js file :
require("dotenv").config(); // will load .env file in process.env
const webpack = require("webpack");
...
plugins: [
...
new webpack.DefinePlugin({
"process.env": {
NODE_ENV: JSON.stringify("production"),
SENTRY_DSN: JSON.stringify(process.env.SENTRY_DSN),
BUILD_DATE: JSON.stringify(new Date()),
TRAVIS_COMMIT: JSON.stringify(process.env.TRAVIS_COMMIT)
}
}),
...
]
Make sure you have strings only as if the value isn't a string, it will be stringified (including functions).
Then in your code, you can call console.log(process.env.NODE_ENV)

Profiles in React

I'm trying to figure out how to run a react application (created through create-react-app) with different profiles.
That is, suppose I have several environments (local, dev, prod) and I have a fetch that refers to the backend (which is deployed on another server).
The backend has its own address for each environment. I need to somehow set global variables for different launches.
For example, in Springboot this can be done via application-"profile".properties.
I run the application through npm install -g serve & serve -s build. How to do it?
When working with create-react-app, you can configure your app using environment variables.
It is explained in detail in the documentation here: https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables
All environment variables need to be prefixed with REACT_APP_.
You can define profiles with different environment variables using .env files.
For example, to set an API URL in production, create a file .env.production with the following contents:
REACT_APP_API_URL=https://my.beautiful.api/
…and as default (for local development), create a file .env:
REACT_APP_API_URL=http://localhost:3001/
The environment variables from the .env.production file will be used when you build your project with npm run build
The environment variables from the .env file will be used when you work on your project in local dev mode with npm start
Example for using the environment variable in your app's code:
fetch(process.env.REACT_APP_API_URL)
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(JSON.stringify(myJson));
});
The way i handle this case is by using package react-native-config and i have create .env file (.env.dev, .env.staging, .env.prod) and i have define some scripts in the package.json. I am using react-native init project though.
as below
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest",
"postinstall": "sed -i '' 's/#import <RCTAnimation\\/RCTValueAnimatedNode.h>/#import \"RCTValueAnimatedNode.h\"/' ./node_modules/react-native/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h",
"clean": "cd android && gradlew clean",
"feature": "node scripts/createfeature.js",
"component": "node scripts/createcomponent.js",
"android": "cd android && gradlew app:assembleDebug && gradlew installDebug",
"storybook": "storybook start -p 7007",
"prestorybook": "rnstl",
"android-dev": "SET ENVFILE=.env.dev && react-native run-android",
"android-staging": "SET ENVFILE=.env.staging && react-native run-android",
"android-prod": "SET ENVFILE=.env.prod && react-native run-android",
"ios-dev": "ENVFILE=.env.dev react-native run-ios",
"ios-staging": "ENVFILE=.env.staging react-native run-ios",
"ios-prod": "ENVFILE=.env.prod react-native run-ios",
"build-android-prod": "SET ENVFILE=.env.prod && cd android && gradlew assembleRelease"
},

Create multiple react builds, based on different configuration files

I have a web app created with create-react-app. Instead of having just one build folder generated via "yarn build" I need to have multiple build folders each using a different configuration file (for database connection etc).
How can I do that?
Yes, it is possible. Just define another build script.
Find script in package.json something like this:
"scripts": {
"start": "node scripts/start.js",
"build": "npm run git-info && node scripts/build.js",
"test": "git-info && node scripts/test.js --env=jsdom",
"git-info": "git log -1 --pretty=format:\"%h%x09%x09%ad%x09%s\" > src/static/gitInfo.txt"
},
You can define something like this:
"scripts": {
"build": "npm run git-info && node scripts/build.js && node scripts/build2.js && node scripts/build3.js",
},
When you call yarn build , its called all commends in build section (npm run git-info && node scripts/build.js && node scripts/build2.js && node scripts/build3.js)
(yarn or npm...)
In your build script scripts/build.js, scripts/build1.js, ... you can define what you want (output folders, etc...)
Assuming you have one configuration file per environment, you could have a build script that takes the config as an argument or that reads from process.env and then you load the right .env file for each build.
"scripts": {
"build": "dotenv-cli -e .env.production node build.js",
"build:staging": "dotenv-cli -e .env.staging node build.js",
"build:dev": "dotenv-cli -e .env.dev node build.js",
}
Here, build.js would be a custom JS file that you wrote and it would build your app.
Keep in mind that you'd need to output the built files in a way that they don't overwrite each other as you build for different environments.

What are these targets in node for?

I have the following in my package json that is responsible for building and running the Angular 2 application. May I know the meaning of each of the flags in the targets ? This for deploying an Angular 2 application in IBM Bluemix
"build": "rimraf dist && webpack --progress --profile --bail",
"start": "tsc && concurrently \"tsc -w\" \"lite-server\" "
The answer to your questions are located in the documentation for each of the node js libraries:
https://www.npmjs.com/package/rimraf
https://www.npmjs.com/package/concurrently
https://www.npmjs.com/package/tsc
https://www.npmjs.com/package/webpack
https://www.npmjs.com/package/lite-server
The build script is deleting the dist folder, then building your app with webpack.
The start script is compiling your typescript and then running the typescript compiler in watch mode and then starting lite-server concurrently.

Resources