How To Deploy React on AWS using NGINX

Written by msokola | Published 2021/03/29
Tech Story Tags: react | aws | docker | nginx | reverse-proxy | deploy-a-web-app | aws-api-gateway | aws-blogs

TLDRvia the TL;DR App

This post will help you to learn how to deploy your React applications to production. We are going to use Docker and NGINX to secure API keys and proxy requests to prevent Cross-Origin Resource Sharing (CORS) violations.
You can find the code and video in the summary
In every project lifecycle, the time comes to publish it, and it is not always that obvious how to do so. The production environment is different than the development one, and users will not take any extra steps to run it. The most of web apps consume some sort of APIs, and often, it is hosted on a different server. In this case, as a developer, we need to solve Cross-Origin Resource Sharing (CORS) issues. Too often, we end up building a backend even it is not necessary. I believe good developers should keep the application simple, and cut off all redundant pieces. In this article, I would like to show you how I prepare my React apps to deploy them to production.
I could make a trivial React example app but it wouldn’t be very helpful. So that I decided to hook my app into a real API provided by FED St. Louis. The API requires an access key to retrieve data, and endpoints are protected against cross-domain requests — no external web app will be able to directly consume data.
Disclaimer: If you application relies on server-side rendering this is not the right deployment strategy. You can get inspired but you will still need some backend.

Preconditions

It is critical to have some basic knowledge of creating React apps, and Docker before you follow the instructions in this article. If you miss anything don’t worry! Just check this amazing article and YouTube tutorial on FreeCodeCamp:

Example React App

I bootstrapped a simple web app using create-react-app. The only job the app has is displaying a line chart with a representation of the GDP of the United States.
The app retrieves data only from the following API:
https://api.stlouisfed.org/fred/series/observations?series_id=GDPCA&frequency=a&observation_start=1999-04-15&observation_end=2021-01-01&file_type=json&api_key=abcdefghijklmnopqrstuvwxyz123456
The parameters:
  • series_id - The id for a series. The GDPCA stands for the "Real GDP".
  • frequency - The aggregation of the data - the a stands for annual.
  • observation_start - The start of the observation period.
  • observation_end - The end of the observation period.
  • file_type - The format of data. Default xml.
  • api_key - The access key required to retrieve any data from this API. You can request one here.
You can find more details in the documentation.
Life isn’t always perfect and the API design is not ideal. It requires the developer to pass the access key and expected output of the data as URL parameters. Passing the output as a parameter is not a problem for us because it only adds some noise — but the leaking API key is. Imagine somebody intercepts them and abuses the API to perform some prohibited action. We don’t want to risk it.
Let’s assume for a moment the API keys are not a problem. Still, it isn’t possible to take advantage of this API. The FRED API is protected against cross-domain requests so that we will get the following errors if we try to call it from an external domain.
Many developers would suggest building middleware (a backend) to proxy requests to the API and filter sensitive data. They would say they might need to add new features in the future, and to a certain degree, it is a fair approach. But I prefer to build my apps in a more YAGNI way (You Ain’t Gonna Need It). So that I’m not going to avoid build the backend until it isn’t necessary — in our case I will not build it at all.

Let’s use NGINX!

I am a big fan of NGINX because it brings simplicity. NGINX has all you need to prepare a production-grade web server such as HTTP2, compression, TLS, and many others. The most important — we can achieve it by defining a few lines of configuration. Just take a look at the snippet below:
...

http {
    ...

    server {
        ...

        location /api {
            set         $args   $args&&file_type=json&api_key=abcdefghijklmnopqrstuvwxyz123456;
            proxy_pass  https://api.stlouisfed.org/fred/series;
        }
    }
}
Those 4 lines are all I needed to hide our API key and suppress the CORS errors. Literally! From now on, all HTTP requests to /api will be proxied to FRED API, and only our apps will be able to consume the API. All external requests will face CORS errors.
To get rid of clutter I replaced all default content of the file with ... (three dots). You can find the full version on my GitHub or video (links below).
And that’s how our endpoint looks like:
/api/observations?series_id=GDPCA&frequency=a&observation_start=1999-04-15&observation_end=2021-01-01
We need to pass neither api_key nor file_type parameters to retrieve data. nobody can read the access key from the URL - it is safe.

Docker loves NGINX

The most convenient way to run NGINX in the cloud would be by using Docker. I assume you know what is Docker if not please read the article linked in the Preconditions.
We just need to create a Dockerfile with the following contents:
FROM nginx

COPY container /
COPY build /usr/share/nginx/html
And now, only three more steps are needed to run the FRED APP:
  • Build React application. This process generates the build/ directory containing static files.
  • Build the Docker image. It will create a runnable Docker image.
  • Publish Docker image to some repository or run it on the local machine.
For now, let’s try to run it on our machine.
$ yarn install
$ yarn build
$ docker build -t msokola/fred-app:latest .
$ docker run -p 8081:80 -it msokola/fred-app:latest
The 8081 is a port on your machine. It means the app will be available under the following URL: http://localhost:8081.
After opening this URL in the browser you suppose to see logs like that in your terminal:
0.0.0.1 - - [11/Mar/2021:18:57:50 +0000] "GET / HTTP/1.1" 200 1556 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36" "-"
...
0.0.0.1 - - [11/Mar/2021:18:57:51 +0000] "GET /api/observations?series_id=GDPCA&frequency=a&observation_start=1999-04-15&observation_end=2021-01-01 HTTP/1.1" 200 404 "http://localhost:8081/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36" "-"
Pay attention to those 200s as they stand for HTTP status OK. If you see a 400 next to the API request it means something is wrong with your API key. The 304 is also fine (it means the data was cached).

Summary

If you are at the beginning of your career you might’ve never deployed your application yet. But it is good to prepare yourself because one day you will need to do so. Every project needs to face users otherwise it will have no chance to be successful, and never pay itself.
“A ship in harbor is safe — but that is not what ships are built for.” — John A. Shedd
On my YouTube channel, you can find an extended version of this tutorial (17 minutes) including deployment to Amazon Web Services (AWS). I decided to skip the deployment in the article because FreeCodeCamp offers a free tutorial on AWS (~5 hours). Also, it would only contain screenshots with short descriptions. I prefer to watch somebody doing it rather than reading plain text with pictures, so here is the video:
You can find all code in this GitHub repository: https://github.com/mateuszsokola/react-to-aws
That is all folks! I hope you liked it and have a great day!

Written by msokola | Senior Software Engineer based in Abu Dhabi, United Arab Emirates
Published by HackerNoon on 2021/03/29