How to add environment variables to AWS amplify? - node.js

I have a React/Node app which i am trying to host on AWS amplify. first try, my app deployed but i saw some pages are not loading due to lack of the environment variables.
i have added them to the AWS console before deploying and it did not work. then i did some search and i saw that i need to modify "amplify.yml" file to:
build:
commands:
- npm run build:$BUILD_ENV
but not only it did not work, also the app is not working anymore.
any ideas?

As this question specifically references React, here are the steps you need to use environment variables in your React based application in AWS Amplify.
In your client-side JS:
const BUILD_ENV = process.env.REACT_APP_BUILD_ENV || "any-default-local-build_env";
The key thing to note here is my pre-fix of REACT_APP_ which is covered the Create-React-App docs: here
Now, in your Amplify console, under App Settings > Environment variables:
Finally, you also need to add this reference under App Settings > Build Settings:
Note: "BUILD_ENV" can be any string you wish. Within the environment variables you can provide the necessary DEV / PROD overrides.
DO NOT store SECRET KEYS using this method, AWS provide a secrets manager for this. This method is for publishable keys, like connecting to Firebase or Stripe and the key is fine to be public.

The Amplify documentation on Environmental Variables has a section on "Accessing Environmental Variables".
Per that documentation, in your Amplify.yml (either in the console or by downloading it to the root of your project), you can use a command to push Amplify Environmental Variables into your .env. If you created an Environmental Variable in Amplify called "REACT_APP_TEST_VARIABLE" you would do...
build:
commands:
- echo "REACT_APP_TEST_VARIABLE=$REACT_APP_TEST_VARIABLE" >> .env
- npm run build
Once in your .env you can access them through process.env...
console.log('REACT_APP_TEST_VARIABLE', process.env.REACT_APP_TEST_VARIABLE)
If you are using Create React App, you already have dotenv, or see Adding an .env file to React Project for more info.
Obligatory reminder to add your .env to your .gitignore, and don't store anything in .env that is sensitive.

To get #leandro's answer working I had to wrap the AWS environment variable names in curly braces:
build:
commands:
- npm run build
- VARIABLE_NAME_1=${VARIABLE_NAME_1}
Probably better as a comment but I don't have enough points to post one.

#A Zarqam Hey man, I ran into this issue ans spent a decent amount of time on it. What worked for me was:
On my React code, use process.env.VARIABLE_NAME
On my webpack.config.js use the following plug-in:
new webpack.EnvironmentPlugin(['VARIABLE_NAME_1', 'VARIABLE_NAME_2'])
On the Amplify environment variables put the VARIABLE_NAME_1,etc and then the values, just like in the docs says.
Last on the build settings:
build:
commands:
- npm run build
- VARIABLE_NAME_1=$VARIABLE_NAME_1
(the one with $ is a reference to the one you put in amplify. Also I think you must have no spaces between the = symbol)
Then trigger a build, and cross your fingers.

Just to add to other comments regarding Secret keys, since SO doesn't let me comment until 50 rep... If you're not using those Env Variables in your front-end app such as process.env.<var_name>, and only use them during build time, you're fine. Those files will not be bundled into your front-end app.
I know this question is related to frontend apps but its title appeared in search engines for me even though I was looking for build variables only, so it might be useful for other people too.

An add on to #leandro's for anyone checking for this in the future I just want to simplify what you need to do since I was completely lost on this:
In your code reference all API keys as process.env.EXAMPLE_API_KEY_1
Run this in your root folder terminal npm install react-app-rewired --save-dev
Add config-overrides.js to the project root directory.(NOT ./src)
// config-overrides.js
module.exports = function override(config, env) {
// New config, e.g. config.plugins.push...
return config
}
Set your variables in AWS Amplify with your key and variable, pretty self-explanatory.
In your build settings make it look something like this (I personally don't add npm build in here but you can if you need to.)
frontend:
phases:
build:
commands:
- EXAMPLE_API_KEY_1=${EXAMPLE_API_KEY_1}
- EXAMPLE_API_KEY_2=${EXAMPLE_API_KEY_2}
Start or restart your build.
I used #leandro's answer and mixed in an answer from this question to get it to work for me.

This worked for me to deploy React to Amplify
amplify.yml
version: 1
frontend:
phases:
preBuild:
commands:
- npm install
build:
commands:
- npm run build
artifacts:
baseDirectory: build
files:
- '**/*'
cache:
paths:
- node_modules/**/*
in App.js
const client = new ApolloClient({
uri:
process.env.NODE_ENV !== 'production'
? 'http://localhost:1337/graphql'
: process.env.REACT_APP_ENDPOINT,
cache: new InMemoryCache(),
});

Related

React boilerplate - changing API url at runtime

I'm using react-boilerplate as the base for my project.
I'm currently defining my API url in webpack.base.babel.js like so:
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
PIZZA_API_URL: JSON.stringify('https://some-ip:8081')
},
}),
However, this is only picked up at build time.
In create-react-app, you can use REACT_APP_PIZZA_URL. Anything that starts with REACT_APP* can be set at the start of runtime.
So I can do:
docker run -e "REACT_APP_DB_HOST=HELLOWORLD" -d -p3000:3000 pizza-supplier-ui:test
How do you do the same thing with react-boilerplate?
Thank you
Ok - here is the answer to my own question after hours of research and trial & error.
For production build, webpack will compile your js files and bundle them up in build/ directory.
The API URLs that are referenced as ${process.env.API_URL}/api/query/findPizzaById/ are baked into the compiled js files in the build/ directory.
Therefore this command:
docker run -e "API_URL=HELLOWORLD" -d -p3000:3000 pizza-supplier-ui:test
has absolutely no effect. It does however register API_URL as an environment variable on the running docker image. The problem here is that API_URL is defined at BUILD TIME, using the environment variable FROM where the build has run.
If anyone has struggled with this issue and have a solution I'd appreciate your enlightenment!
Thanks

How to make build for react app for different stages?

I have a single page application which is a react app. I am using webpack for it. I am facing problem in configuring server API URL for every stage like test, beta and prod.
Is there some standard way of doing it?
Create a .env and add your variables there ensuring that they are prefixed with REACT_APP e.g. REACT_APP_SERVER_URL=https://example.com
You can create multiple env files one each for dev, prod, test etc. like .env.local, .env.prod
The env files injected from your npm commands
npm start: .env.development.local, .env.development, .env.local, .env
npm run build: .env.production.local, .env.production, .env.local, .env
Use the variable in your code like
if (process.env.NODE_ENV !== 'production') {
analytics.disable();
}
OR
<b>{process.env.NODE_ENV}</b>
Refer https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-development-environment-variables-in-env
Do that based on NODE_ENV.
declare an url file in your application for the basepath of it.
const baseURL = (__DEV__) ? url1 : url 2;
or a switch statement, does'nt matter.
Do be able to have access to these variables, you have to use DefinePlugin from webpack.
new webpack.DefinePlugin({
envType: JSON.stringify(process.env.NODE_ENV)
})
or something like that...

Using environment variables in Node

I have been trying to get a streamline way of having different environment variables for local and production web apps, but I haven't come across the "ideal" solution yet.
There's the option of having a config.js file like so:
//config.js
{
"secretKey": "SDFDASFFSFD",
"facebook": {
"clientID": "EFGFDGBGDGFS",
"clientSecret": "EGDFNHFG"
}
}
And accessing via ES6 imports
Or using .env files like so:
SOME_KEY=someValue
HELLO=world
FACEBOOK_SECRET=435SDFSF5DZVD7S
And accessing the variables via process.env in the code using dotenv.
Obviously no matter what route you go down, the file will need to be omitted from version control which is fine. Each of these ways are great, but they only seem to work well for local development.
So how do you then have a separate file for a production environment? The dotenv docs say they strongly recommend against a .local.env and .prod.env situation.
Also, how is best to push to a remote server? I have my own server with Gulp tasks which run on a Git post-receive hook. How is best to pass up the production environment variables to here?
Thanks
You could have own config file for each environment:
- environments
- index.js
- deveplopment.json
- staging.json
- production.json
To use appropriate config file, run the app with required NODE_ENV:
NODE_ENV=production node index
In environments/index.js determinate the current NODE_ENV and use config:
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
module.exports = require('./' + process.env.NODE_ENV);
If config file doesn't include secret info (apiKeys, etc), it can be pushed to repo. Otherwise add it to .gitignore and use environment variables on the server.
Note:
For advanced configuration use such packages as nconf.
It allows to create hierarchical node.js configuration with files, environment variables, command-line arguments, and atomic object merging.
Have you thought about having a keys.js file with a .gitignore on it?
I've used this in the past to use module.exports{} to get my variables usable but not going to version control for the security side of things!

create react app cannot read environment variable after build

I have a react app , created with create-react-app then I build the app with command: npm run build
It's using serve to run the app after build, if we start the app with development code by running ENV=production npm run start it can read the process.env.ENV variable beacause I'm adding this plugins to webpack dev config
new webpack.DefinePlugin({
'process.env':{
'ENV': JSON.stringify(process.env.ENV),
}
}),
I also add the script above to webpack prod config, but if I try this command after build ENV=prod serve -s build, it cannot read the environment variable
How to fix this?
If you set all the environment variables inside the app.config.js, you can replace them after the build in the main.????????.chunk.js file.
A sample app.config.js could look like:
export default {
SOME_URL: "https://${ENV_VAR_1}"
SOME_CONFIGURATION: "${ENV_VAR_2}",
}
Leave the app.config.js file as is, without replacing the environment variables with their actual values. Then, create the optimized production build:
npm ci # if not already installed
npm run build
If the default webpack configurations are used, the contents of app.config.js will be bundled in build/static/js/main.????????.chunk.js. The values of the environment variables can be be envsubst, with a bash script like this:
main_chunk=$(ls build/static/js/main.*.js)
envsubst <$main_chunk >./main_chunk_temp
cp ./main_chunk_temp $main_chunk
rm ./main_chunk_temp
Note: In the above example, envsubst reads the actual variables set in the environment at runtime and literally replaces ${ENV_VAR_1} and ${ENV_VAR_2} with them. So, you can only run this once as the chunk is being over-written.
The reason why you can not read the ENV var is because:
(1) In development mode webpack watches your files and bundles you app on the fly. It also will read (because of the DefinePlugin) your process.env.ENV and will add it as a global variable. So it is basically piping variables from process.env to your JS app.
(2) After you've build your app (with webpack) everything is already bundled up into one or more files. When you run serve you just start a HTTP server that serves the static build files. So there is no way to pipe the ENV to you app.
Basically what the DefinePlugin does is add a var to the bundle. E.g.
new webpack.DefinePlugin({
'token': '12356234ga5q3aesd'
})
will add a line similar to this:
var token = '12356234ga5q3aesd';
since the JS files is static there is no way to change this variable after you've build/bundled it with webpack. Basically, when you do npm run build you're creating the compiled binary/.dll/.jar/... file and can no longer influence its contents via the plugin.
You can add a .env file to the root of your project and define your environment variables there. That will be your default (production) environment variables definition. But then you can have a local file called .env.local to override values from the default.
When defining your environment variables, make sure they start with REACT_APP_ so your environment variable definitions would look like this:
REACT_APP_SERVER_URL=https://my-awesome-app.herokuapp.com
Also, add this to .gitignore so you don't commit your local overrides:
.env*.local
Reference:
Adding Development Environment Variables In .env (create-react-app)
From create-react-app documentation:
Your project can consume variables declared in your environment as if
they were declared locally in your JS files. By default you will have
NODE_ENV defined for you, and any other environment variables starting
with REACT_APP_.
You can read them from process.env inside your code:
render() {
return (
<div>
<small>You are running this application in <b>{process.env.NODE_ENV}</b> mode.</small>
<form>
<input type="hidden" defaultValue={process.env.REACT_APP_NOT_SECRET_CODE} />
</form>
</div>
);
}

How to set some flags on my node build

When I build, test and deploy my node application from Codeship to Heroku I want to be able to set a release flag to true using a command line during the build. And in my code I want to do something like this....
if(config.release) load(liveConnection);
else load(debugConnection);
How can I achieve this? Is there some sort of package I install to run a build script which will transform my config file?
Instead of using a config file, you should use environment variables. For example:
heroku config:set NODE_ENV=production
Then, in node:
if (process.env.NODE_ENV === 'production') load(etc);
An even better way is to just provide connection information uniformly, through config files, like this:
heroku config:set CONNECTION_STRING=foo
Then in node:
load(process.env.CONNECTION_STRING);
That way, the environment is providing the config. Locally, you can start the app with a development string like CONNECTION_STRING=some_debug_string node server.js, or you can use a .env file to provide a whole set of them. More info here:
http://12factor.net/config

Resources