React API Tutorials

7 Ways to Deploy a React App

Jump to

Where Should I Deploy My React App?

Deploying your newly created React App typically falls at the hands of whoever is creating the tutorial or example application that you are following. Sometimes this works out fine because they typically have experience with that deployment option, or the deployment option is easy to follow.

Nonetheless, at some point, you are going to wonder what other options are out there and if your future app could benefit from various deployment options.

Today, we are going to take a simple React application and deploy it to several services covering different options. I hope that the application and deployments can widen your understanding of how different apps fit into deployment scenarios.

Common Considerations

Workflow

The workflow consideration usually is the result of the question, how much work am I willing to do? Many cloud providers have found success by offering quick and painless ways to get an application into production that is easy to manage.

Managing a production server can be a lot of work with the amount of configuration, monitoring, and updating that takes place over the server’s lifetime. However, sometimes the application requires this kind of setup. Later, we are going to cover setting up a cloud server running Ubuntu and deploy a React app to that remote server.

Furthermore, there are considerations with continuous integration (CI) and continuous deployment (CD). Does your app require a separate Jenkins server for handling these desirable workflow features? Or, can you choose a deployment option that automatically integrates with your Git repository?

Application Type

Another consideration is the supported application types. Some services only support certain languages. Furthermore, other services are more designed towards certain stacks (i.e JAMstack, MERN, LAMP). These different stacks reflect the application’s language, data storage type, etc. It’s worthwhile to deploy in a way that fits the style of your application.

Security

One of the first considerations that I have when choosing a deployment option is what levels of security do I need? Many applications need to maintain secret keys to allow access to resources or conceal user information.

There are many tutorials available for deploying React applications that do not include the use of third-party API keys. Consequently, one of the most important aspects of any application (security) can have an effect on how the application is designed.

Pricing and Performance

The overarching consideration is how much the deployment will cost. This is usually attached to the desired performance of the application and the desired geographic outreach. Thankfully, most cloud deployment options are transparent about their pricing and have free options to test the service before committing.

7 Ways to Deploy a React App

In the rest of the article, we are going to take a simple React application and deploy it in numerous ways. The application is a single page and makes an API call. It will be transformed to fit different deployment scenarios.

The deployments do not include static site options like Gatsby.js or server-side-rending frameworks like Next.js. However, below is a list of tutorials that can get you started using Gatsby or Next.js:

Gatsby.js

Next.js

One static option we will talk about is deploying to Github Pages. This application will not make the third-party API call, but if you used a different provider, like AWS, Azure, Firebase, as demonstrated in the other application deployments, then you could set up a function call to serverless cloud function.

There are a few prerequisites to cover, but after that, we will be ready to begin!

Prerequisites

  • Git installed and set-up
  • Internet access
  • Basic experience using Git and the command line
  • Basic experience with React.js and Node.js
  • RapidAPI Account. Visit RapidAPI to get signed up for a free account if you haven’t already!
  • Subscription the Aeris Weather API
    • Visit the Pricing page to subscribe.
    • The free plan allows for 100 API requests a day

Forking the Starter Repository

Open up a new browser and navigate to https://github.com/jdretz/rapidapi-deply-react-app

Towards the top right of the screen, hit the Fork button. A copy of the repository will be added to your Github account.

Next, clone the repository from your Github account to your local system.

git clone https://github.com/yourusername/rapidapi-deply-react-app.git

Open up a new terminal in your cloned project root and run npm install.

You are now ready to make changes and push those changes up to your new repository. If you plan on doing multiple deployments with the repository you could delete it after each deployment and re-fork the repository or work on your Git skills and revert it to a previous commit.

1. How to deploy a React App on Heroku

Sign up for an account on Heroku.

Download the Heroku-CLI for your platform or using the command line with npm install -g heroku.

After downloading, open up a new terminal and  configure the Heroku-CLI by entering heroku login into the terminal.

Our Heroku deployment requires a Node backend to complete our API call.

Therefore, we are going to rearrange our project structure and add a Node.js backend.

After following the forking instructions above, open up the project with your text editor.

1. Set-Up Node.js Server

In the root of the project create a folder named client. Move all of the visible files into that folder.

Next, in the project root, create the files;

  • server.js
  • .gitignore

Then, add the code below to the corresponding file.

server.js

const express = require('express');
const path = require('path');
const axios = require('axios');

// Loads env variables
require('dotenv').config()

const app = express();

const PORT = process.env.PORT || 3001;

// Adds json parsing middleware
app.use(express.json());

// Setup static directory to serve
app.use(express.static(path.resolve('client', 'build')));

// Creates weather endpoint
app.post('/weather', async (req, res) => {
    const { location } = req.body

    // Encode the variable so we can send the location in a URL
    const encodedLocation = encodeURIComponent(location)

    try {
        // Call the Weather API
        const { data } = await axios({
            method: "GET",
            url: `https://aerisweather1.p.rapidapi.com/observations/${encodedLocation}`,
            headers: {
                "content-type": "application/octet-stream",
                "x-rapidapi-host": "aerisweather1.p.rapidapi.com",
                "x-rapidapi-key": process.env.RAPIDAPI_KEY,
                "useQueryString": true
            }
        })

        // Pull the information that we need from the Weather API response
        const weatherData = {
            conditions: data.response.ob.weather,
            tempC: data.response.ob.tempC,
            tempF: data.response.ob.tempF
        }

        // Return the data object
        return res.send(weatherData)
    } catch (e) {
        console.log(e)

        return res.status(500).send('Error.')
    }
})

app.get('*', (req, res) => {
    res.sendFile(path.resolve('client', 'build', 'index.html'));
});

// console.log that your server is up and running
app.listen(PORT, () => console.log(`Listening on port ${PORT}`));

.gitignore

node_modules
.env
package-lock.json.*

Finally, navigate into your server folder using the terminal. Inside the server directory execute the commands,

$ npm init -y
$ npm install express axios dotenv --save

After the packages are installed, there is only one more change to be made to the project. Open up client/src/App.js and change the URL in line 18 of the file to /weather.

Good work, our API function is now on our backend server. Let’s deploy to Heroku!

2. Deploy to Heroku

In server/package.json, under scripts add a start script and post-build script.

"scripts": {
  "start": "node server.js",
  "heroku-postbuild"::"cd client && npm install && npm run build"
  ...
},

Next, create a file named Procfile in the project root and add this line to it.

web : npm run start

Push the changes that we just made to your Git repository.

Now, we are ready to create the Heroku app!

Make sure that your terminal is in the project root and run the command heroku create.

This command returns the app name (i.e “floating-coast-11273″), URL, and Git remote URL. 

Now that we have the app name we can add in our RapidAPI-key environment variable.

Locate your API key on RapidAPI. Then, run the command, inserting your RapidAPI key.

heroku config:set RAPIDAPI_KEY=yourkey

Finally, we can push the changes to the Heroku remote Git repository and our app will be built.

In the project root run:

git push heroku master

The app will build and output the URL for the new application, again.

Great work! I hope that was easy!

Remember your app is now public, you can log onto Heroku and delete or deactivate your application if you don’t want people to run up your API request usage.

2. How to deploy a React App on Apache server (Linode)

The Apache deployment requires a Node backend for our API call, but if we use Linode we can simplify the deployment process. Then, after deployment, we can make the custom changes that we need by accessing the remote server.

Despite simplifying the process, running a cloud production server requires more configuration. Consequently, there is increased risk. I have made a Linode StackScript available to help mitigate some of the risks with the process.

Furthermore, when deploying to your own remote server, we are doing more manual tasks. Some cloud providers set up nice continuous deployment workflows, but this setup is very hands-on.

Follow the forking instructions above and then complete the first step in the Heroku deployment to complete the Node.js (Express) back-end set up. When completed, push your changes up to the Git repository.

Sign up on Linode and then visit your Cloud Dashboard.

1. Configure and Deploy the Server

Once on the dashboard select StackScripts on the left sidebar. On the top right of the page click the Create New Stackscript. Name it whatever you like, and write a brief description.

Choose Ubuntu 18.04 for the image and paste in the below code to the script.

#!/bin/bash
# <UDF name="project" Label="Name of Project" example="personal_blog" />
# <UDF name="username" Label="Create New User" example="username" />
# <UDF name="password" Label="New User Password" />
# <UDF name="hostname" Label="Change Hostname" example="localhost" />
# <UDF name="sshkey" Label="Add GPG Public Key" example="AAAAB3NzaC1yc2EAAAADAQABAAACAQCdZBMH9fKg995K" default=""/>
# <UDF name="disableroot" Label="Disable Login with Password & Disable root user Login?" oneOf="Yes,No" default="Yes" />
# <UDF name="timezone" Label="Set Date Time Zone" oneOf="EST, UTC, MST" default="EST" />
# This sets the variable $IPADDR to the IP address the new Linode receives.
IPADDR=$(/sbin/ifconfig eth0 | awk '/inet / { print $2 }' | sed 's/addr://')
echo "    ~~~~ Running Ubuntu18LTS configuration ~~~~"
#Update
sudo apt-get update
# Set date/Time to EST
echo $TIMEZONE | sudo timedatectl set-timezone $TIMEZONE
date
echo $USERNAME
if [ "$USERNAME" != "" ]
then
  echo $USERNAME | adduser --disabled-password --shell /bin/bash --gecos "User" $USERNAME
  echo $USERNAME:$PASSWORD | chpasswd
  sudo adduser $USERNAME sudo
  # SSH Harden
  sudo mkdir -p /home/$USERNAME/.ssh
  sudo chmod -R 700 /home/$USERNAME/.ssh
  sudo chown $USERNAME:$USERNAME /home/$USERNAME/.ssh
  sudo touch /home/$USERNAME/.ssh/authorized_keys
  # GPG Public Key
  sudo echo $SSHKEY >> /home/$USERNAME/.ssh/authorized_keys
  cd ~
fi
echo $HOSTNAME
# Check if hostname's been provided
if [ "$HOSTNAME" != "" ]
then
  hostnamectl set-hostname $HOSTNAME
  # Add hostname to Hosts
  myip="$(curl ifconfig.me)"
  sudo echo "$myip   $HOSTNAME" >> /etc/hosts
  echo "Host name changed to: " $HOSTNAME
fi
echo $DISABLEROOT
if  [ "$DISABLEROOT" == "Yes" ]
then
  # Make sure a user is provided in order to prevent being locked out
  if [ "$USERNAME" != "" ]
  then
    # Disable root login
    echo "Disabling root login"
    sudo sed -i -e 's/PermitRootLogin yes/PermitRootLogin no/g' /etc/ssh/sshd_config
    # If GPG key is provided disable login by password
    if [ "$SSHKEY" != "" ]
    then
      sudo sed -i -e 's/#PasswordAuthentication yes/PasswordAuthentication no/g' /etc/ssh/sshd_config
    fi
  fi
fi
# Install Apache
sudo apt install apache2 -y
sudo mkdir /var/www/$PROJECT
sudo chown -R $USERNAME:$USERNAME /var/www/$PROJECT
sudo chmod -R 755 /var/www/$PROJECT
sudo touch /etc/apache2/sites-available/$PROJECT.conf
sudo a2enmod proxy proxy_http rewrite headers expires
sudo a2dissite 000-default
#Install Node and NPM and other dependencies
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt-get install -y nodejs
sudo apt install build-essential
sudo npm install pm2@latest -g
# Set Up Firewall
sudo apt-get install ufw -y
sudo ufw default allow outgoing
sudo ufw default deny incoming
sudo ufw allow ssh
sudo ufw allow 80
sudo ufw status
sudo ufw --force enable
echo "    ~~~~~~~~ Ubuntu 18 LTS configuration is complete ~~~~~~~~"
sudo reboot

There are a lot of commands that go into setting up a server. The above script:

  • configures the timezone, hostname, IP address
  • creates a superuser with your username and password
  • sets up SSH keys for remote server access authorization
  • downloads Node, NPM,  PM2, and other dependencies
  • disables root password login
  • blocks all incoming traffic except on ports 22 (SSH port) and 80 (HTTP)
  • installs and does some configuration for Apache webserver

Save the script.

Next, select the newly created StackScript and, in the top right, hit the Deploy New Linode button.

Fill out the information in the input fields. The project name is used as a folder name on the server so it cannot have spaces.

Then, to set up the remote SSH you must input a public SSH key that you have on your computer.

This key must be generated, and on a Mac can typically be found in the /.ssh/ folder. However, some systems change, and not everyone uses the same operating system. This is the point in the process where you will most likely have difficulty if you have never accessed a remote server before.

For Mac users, this is a helpful post on Stack Overflow in terms of getting your public key.

For Windows, you are probably better off with this Youtube search. You will have to install and configure PuTTY.

Once you have your public key pasted into the form (and on your computer) finish filling out the form with these values.

  • Disable root login
  • Select Ubuntu 18.04 LTS
  • Pick the closest region to your location.
  • Select Nanode for Linode plan. It’s the cheapest option at $5 a month, and you can deactivate or delete the Linode as soon as we finish deploying.
  • Enter a root user password

Before creating the Linode you need a credit card on file for your account.

Hit Create.

The Linode takes a little bit of time to boot.

2. Set Up Apache Server

The indicator circle in the top right will be green and display Online when the Linode is ready. Select the Networking and copy your new IP address.

Open up a new terminal and SSH into the remote server. On Mac, this can be done with:

ssh the_username_you_entered_earlier@ip_address

Give it one or two tries before starting to troubleshoot. Sometimes the server isn’t ready yet.

Once logged into the server open up the project configuration file for Apache. The file name will be the name of the project that you entered earlier.

sudo nano /etc/apache2/sites-available/projectname.conf

Once in the file copy and paste the code below.

<VirtualHost *:80>
    ServerName domain.com
    ServerAlias www.domain.com

    ProxyRequests Off
    ProxyPreserveHost On
    ProxyVia Full

    <Proxy *>
        Require all granted
    </Proxy>

    ProxyPass / http://127.0.0.1:3001/
    ProxyPassReverse / http://127.0.0.1:3001/
</VirtualHost>

The code sets up a proxy so requests made to port 80 are redirected to port 3001. This is the port that our Node server is listening on.

To enable the site run sudo a2ensite projectname.conf.

To save the file, hit Crtl+X, then Y, and finally, Enter.

3. Clone Repository

Next, we need to pull our project code to the server and build the project. Change directories into /var/www/projectname with the cd command.

Clone your Git repository with the command below.

git clone https://github.com/yourusername/rapidapi-deply-react-app.git

You may have changed the repository URL for your account.

Next, we need to install the Node packages for the front-end and back-end and run a build.

Execute the commands:

$ cd rapidapi-deply-react-app/
$ npm install
$ cd client && npm install 
$ npm run build

The first command may be different if your repository or folder has a different name.

Once it’s done building, change directories back the repository folder (i.e rapidapi-deply-react-app).

cd ..

4. Start Application

Before starting the app we need to configure our environment variable that holds the RapidAPI key.

Create a new file name ecosystem.config.js.

touch ecosystem.config.js

Inside that file, paste the following code.

module.exports = {
  apps : [
      {
        name: "react_deploy",
        script: "./server.js",
        watch: true,
        env: {
            "PORT": 3001,
            "RAPIDAPI_KEY": "yourapikey",
            "NODE_ENV":"production"
        }
      }
  ]
}

Be sure to replace yourapikey with your actual API key.

Save the file.

This file is specific to the PM2 library.

To start the application run the command below.

sudo systemctl start apache2 && pm2 start ecosystem.config.js

Finally, you can visit the IP address that you copied earlier in the browser to find our app.

You have done some great work if you made it this far! However, the app can still use a little work. Unfortunately, I won’t be going into setting up a custom domain or configuring an SSL certificate. However, you can use the instructions on Let’s Encrypt to get a free SSL certificate once you have a domain name.

Don’t forget to deactivate or delete your Linode in the dashboard so you do not any incur any extra charges. 

3. How to deploy a React App on Firebase

Firebase has many of the same options when deploying a React app as AWS and Azure. With Firebase, we can set up a statically hosted application that makes API calls to a cloud function that renders dynamic content on our site.

First, follow the forking directions above to get the starter project. Then open up the project in your text editor with a new terminal in the project root.

You will need a Google Account before proceeding.

1. Set-up Firebase Locally

Install the Firebase CLI.

npm install -g firebase-tools

Log in to Firebase with your Google Account.

firebase login

Make sure your terminal is in the project root and initialize a new Firebase project.

firebase init

Next, you will be taken through a series of prompts.

For the project set-up select;

Create a new project

Next, specify the name of your new project when prompted.

When asked, What would you like to call your new project?  hit enter to use the default.

Then, in the hosting set-up prompts, select the options that I made in the below image.

Next, in the project root, execute npm run build.

2. Deploy to Firebase

When the build completes run firebase deploy. When the project is done deploying there should be a URL to visit the new project. Our app should appear when you visit the page.

Next, let’s add a function that will call our API. This will allow us to secure our API key and it will allow our app to display dynamic content.

3. Add Firebase Function

Add functions to the project.

firebase init functions

Next, follow the prompts and respond with the answers from the image below.

Then, change directories into the functions directory with cd functions

Install Axios.

npm install axios

After Axios is installed we can add our RapidAPI secret key to the function configuration with the following line. Enter the below command into the terminal replacing apikey with your RapidAPI key.

firebase functions:config:set rapidapi.key="apikey"

This is how we can add environment variables to functions. Finally, replace all the code in functions/index.js with the code below.

const functions = require('firebase-functions');
const axios = require('axios')

exports.weather = functions.https.onRequest(async (req, res) => {

    if (req.method !== "POST") {
        return res.status(405).send('Method not allowed')
    }

    // Assign the location value to the variable location from the body object
    const { location } = req.body

    // Encode the variable so we can send the location in a URL
    const encodedLocation = encodeURIComponent(location)

    try {
        // Call the Weather API
        const { data } = await axios({
            method: "GET",
            url: `https://aerisweather1.p.rapidapi.com/observations/${encodedLocation}`,
            headers: {
                "content-type": "application/octet-stream",
                "x-rapidapi-host": "aerisweather1.p.rapidapi.com",
                "x-rapidapi-key": functions.config().rapidapi.key,
                "useQueryString": true
            }
        })

        // Pull the information that we need from the Weather API response
        const weatherData = {
            conditions: data.response.ob.weather,
            tempC: data.response.ob.tempC,
            tempF: data.response.ob.tempF
        }
        // Return the data object
        return res.send(weatherData)
    } catch (e) {
        console.log(e)

        return res.status(500).send('Error.')
    }

  });

The key is imported on the functions object and is accessed with functions.config().rapidapi.key.

We need to add a rewrite rule so our function has a convenient URL. In firebase.json, add the following rewrite rule above the /index.html rule.

{
  "source": "/weather",
  "function": "weather"
},

Now, the rewrites section of your firebase.json file looks like,

  "rewrites": [
    {
      "source": "/weather",
      "function": "weather"
    },
    {
      "source": "**",
      "destination": "/index.html"
    }
  ]
},

Then, change the API call URL on line 18 of  App.js to /weather.

Finally, redeploy your hosting and functions with,

firebase deploy.

4. Upgrade Plan for Outbound Networking

Firebase cloud functions require a pay-as-you-go subscription for functions that use outbound networking. The free tier covers functions that you use Google services. Therefore, to send the request to the Aeris Weather API you need to upgrade your account.

You can see the pricing details and upgrade them by following this link.

However, once you upgrade, the statically hosted app can make an API call to our cloud function that, subsequently, makes an API call to the weather API for data. The weather data is returned and displayed on the page!

4. How to deploy a React App on Github

Github Pages is a static website hosting service. This means it takes HTML, CSS, and Javascript files and hosts them as individual pages. This can be useful for documentation, or a portfolio, but requires more work if were hoping to make an API call from our front-end.

Therefore, we can deploy a React application with ease, but cannot set up a simple Node backend to secure our third-party API call or use a built-in serverless function (like some of the other deployment options).

We could set up a serverless function on a different service and configure the cross-origin resource sharing (CORS) to only allow access from our domain. Nonetheless, let’s deploy the application.

First, follow the instructions to fork the application.

1. Install and Configure Github Pages

Install the Github Pages library.

npm install gh-pages --save-dev

Next, open up package.json and paste in the homepage property at the top of the object.

"homepage": "https://yourusername.github.io/yourrepositoryname",

You will need to locate your username and repository name to modify the URL accordingly.

Next, add the following properties to the script object.

"scripts": {
  // ... other commands
  "predeploy": "npm run build",
  "deploy": "gh-pages -d build"
},

2. Deploy to Github Pages

Push your changes to your repository.

$ git add package.json package-lock.json
$ git commit -m "deploy setup for Github Pages"
$ git push

In a terminal located at the root of the project (where you installed gh-pages in the previous step), run the command npm run deploy.

This creates a new branch that hosts your React app named gh-pages.

3. Confirm Deployment on Github

Open a browser and navigate to your Github repository.

Click on the Settings tab and scroll down to the Github Pages section.

The Source attribute needs to be set to the gh-pages branch. You can also find your site URL and customize the domain name.

If you visit the page the API call for weather data will not work. There is not a function set up to fetch the data because we cannot secure our API key on the front end.

The Axios API call URL in App.js could be modified to call a serverless function set up on a cloud provider. Check out one of the other deployment options for setting up serverless functions.

Regardless, it’s very easy to turn a React app into a website with Github Pages.

5. How to deploy a React App on AWS Amplify

AWS is a cloud hosting service. The deployment our the React App is going to be similar to our deployments for Azure and Firebase. We are going to host the static files on a storage system and make API calls to serverless functions. This is a great option for our application, and can further be expanded to incorporate server calls for database records if the app progresses.

Sign Up for an account on Amazon Web Services.

Fork the repository following the instructions above.

1. Set Up AWS Amplify

Open up the project in your text editor and open a new terminal the root of the project directory.

Install AWS packages.

npm install aws-amplify aws-amplify-react --save

Install the Amplify CLI-tools globally

npm install -g @aws-amplify/cli

Configure the Amplify CLI

amplify configure

This will start a series of prompts.

First, sign in to your AWS administrator account and specify a region that is close to you.

Then, create a username for the new programmatic user that will be connected to your local computer. To use the default, hit enter.

This pulls up the user creation page on AWS. Give the new user AministratorAccess.

Stop when you see the page with the ACCESS ID and Secret key

In the terminal, you are prompted to enter the Access key ID and the Secret access key. The secret access key is only available once, so make sure you do not navigate away from this page until after you enter the secretAccessKey in the user prompt.

Name the profile “default”.

Now that we have an Amplify user we can initialize an Amplify project. In the root of the directory enter the command below.

amplify init

Again, we have a series of prompts to fill out.

The two red arrows point to aspects of the prompts that may be different for your situation. The red box outlines the name of the user profile that we created when we configured the Amplify CLI.

Later, we use the aws-amplify library to call a Lambda function. However, this needs to be configured in the front-end React application.

Open src/index.js and add the three lines below that configure the client.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import Amplify from 'aws-amplify'      // ADD
import config from './aws-exports'     // ADD

Amplify.configure(config)            // ADD

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

Now that we have initialized an Amplify project we can add the backend API.

2. Add Backend API to Project

It’s time for another command and another series of prompts!

Run amplify add api in the project root.

Then, apply the following answers to the prompts.

There is now a local Lambda function that we can edit before we push it up to our Amplify project.

3. Create Weather API Function Call

A new folder named amplify gets created (if you ran the commands from the root of the project) in our React project. Open up the file at amplify/backend/function/src/app.js.

Notice that this is the Express function app that was created in the previous step. The file has function examples that use the base API route we entered in the prompts (weather).

Replace all the code in amplify/backend/function/src/app.js with the code below.

/* Amplify Params - DO NOT EDIT
  ENV
  FUNCTION_CALLWEATHERAPI_NAME
  REGION
Amplify Params - DO NOT EDIT *//*
Copyright 2017 - 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at
    http://aws.amazon.com/apache2.0/
or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.
*/
var express = require('express')
var bodyParser = require('body-parser')
var awsServerlessExpressMiddleware = require('aws-serverless-express/middleware')
var axios = require('axios')

// Import get secrets function
var secret = require('./secret-manager')

// declare a new express app
var app = express()
app.use(bodyParser.json())
app.use(awsServerlessExpressMiddleware.eventContext())

// Enable CORS for all methods
app.use(function (req, res, next) {
  res.header("Access-Control-Allow-Origin", "*")
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
  next()
});

app.post('/weather', async function (req, res) {
  // Assign the location value to the variable location from the body object
  const { location } = req.body

  // Encode the variable so we can send the location in a URL
  const encodedLocation = encodeURIComponent(location)

  try {
    // Call AWS Secrets to get RapidAPI key
    const secretObj = await secret()

    // Call the Weather API
    const { data } = await axios({
      method: "GET",
      url: `https://aerisweather1.p.rapidapi.com/observations/${encodedLocation}`,
      headers: {
        "content-type": "application/octet-stream",
        "x-rapidapi-host": "aerisweather1.p.rapidapi.com",
        "x-rapidapi-key": secretObj["RAPIDAPI-KEY"],
        "useQueryString": true
      }
    })

    // Pull the information that we need from the Weather API response
    const weatherData = {
      conditions: data.response.ob.weather,
      tempC: data.response.ob.tempC,
      tempF: data.response.ob.tempF
    }

    // Return the data object
    return res.send(weatherData)
  } catch (e) {
    console.log(e)
    return res.status(500).send('Error.')
  };
});

app.listen(3000, function () {
  console.log("App started")
});

// Export the app object. When executing the application local this does nothing. However,
// to port it to AWS Lambda we will create a wrapper around that will load the app from
// this file
module.exports = app

This file now implements the API call the weather API for our app. However, Axios is not installed and it is used in the function. In addition, we have not implemented the secret-manager file, but that will be done in a later step.

In the terminal, change directories into the amplify/backend/function/src folder. This folder is an initialized Node package. We know this because there is a package.json file. Therefore, we can download Node packages here to use in our functions.

Install the Axios library so it can be used in our function.

npm install axios --save

While we are here, we need to install the aws-sdk for accessing secrets later.

npm install aws-sdk --save

4. Modify the Client API Call

We need to call our new function in src/App.js. However, in an Amplify application, this is done differently then the current API call set up.

Instead of using Axios, we use the aws-amplify library that we installed earlier.

Import the API object from aws-amplify at the top of the page with the other imports.

import { API } from 'aws-amplify';
Replace the fetchWeather function in App.js with the code below.
const fetchWeather = (e) => {
  e.preventDefault()
  setLoading(true)
  setError(false)

  const myInit = {
    body: {
      location
    },
  };

  //make edit to redeploy
  API.post('weatherAPI', '/weather', myInit)
    .then((data) => {
      setTempC(data.tempC)
      setTempF(data.tempF)
      setConditions(data.conditions)
    })
    .catch(e => {
      setError(true)
      console.log(e)
    })
    .finally(() => {
      setLoading(false)
    })
}

It’s important to note that the first two arguments in the post function depend on the values we entered into the prompts when we created the function.

5. Add API Key with AWS Secrets Manager

You may notice later in the Amplify Console that there is a place to add environment variables. Unfortunately, these variables are not available in the Lambda function that we just set up. Therefore, we need to establish a workaround.

The workaround that we are going to implement was suggested by Troy Goode on the Amplify-CLI issues page on Github.

On your AWS account, open up the Secrets Manager service

Create a new secret named rapidapi that holds the value of your RapidAPI key.

Back in the project, in the folder amplify/backend/function/callWeatherApi/src, create a file name secret-manager.js. Inside of the file, add the code:

const AWS = require('aws-sdk')

module.exports = async () => {
  const secretsManager = new AWS.SecretsManager()
  const secret = await secretsManager.getSecretValue({ SecretId: 'rapidapi' }).promise()
  if (!secret) {
    throw new Error('Secret not found')
  }
  return JSON.parse(secret.SecretString)
}

This gives the /weather endpoint the ability to call the Secrets Manager for our secret key. However, the Lambda function does not currently have access to that secret. We can give the Lambda function the access it needs in the amplify/backend/function/callWeatherApicallWeatherApi-cloudformation-template.json file.

Open up the callWeatherApicallWeatherApi-cloudformation-template.json file.

This is a JSON configuration file that controls aspects of our ”cloud stack” that is implemented when the application is deployed. Furthermore, it can control IAM access for various resources.

There is a Resources parameter that contains an object. On that object there is a parameter named lambdaexecutionpolicy. Further down in this object exists the PolicyDocument object that holds various policy statements in a Statements array.

Add the object below to the array.

{
     "Effect": "Allow",
     "Action": [
         "secretsmanager:GetSecretValue"
     ],
     "Resource": {
         "Fn::Sub": [
             "arn:aws:secretsmanager:${region}:${account}:secret:rapidapi-RLE73o",
             {
                 "region": {
                     "Ref": "AWS::Region"
                 },
                 "account": {
                     "Ref": "AWS::AccountId"
                 }
             }
         ]
     }
 }

The following line may differ depending on your secret’s name:

"arn:aws:secretsmanager:${region}:${account}:secret:rapidapi-RLE73o"

You can double-check the value by clicking on the secret on the Secrets Manager page.

The PolicyDocument object should look like the code below.

"PolicyDocument": {
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": {
                "Fn::Sub": [
                    "arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*",
                    {
                        "region": {
                            "Ref": "AWS::Region"
                        },
                        "account": {
                            "Ref": "AWS::AccountId"
                        },
                        "lambda": {
                            "Ref": "LambdaFunction"
                        }
                    }
                ]
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue"
            ],
            "Resource": {
                "Fn::Sub": [
                    "arn:aws:secretsmanager:${region}:${account}:secret:rapidapi-RLE73o",
                    {
                        "region": {
                            "Ref": "AWS::Region"
                        },
                        "account": {
                            "Ref": "AWS::AccountId"
                        }
                    }
                ]
            }
        }
    ]
}

This the access rights granted, we are ready to initialize the deployment process.

6. Deploy to Amplify Hosting

First, push the changes that you have made to your Git repository.

We are now ready to deploy our application.

In the project root, add hosting to services with the command below.

amplify add hosting

Again, select the two options below when prompted that are displayed in teal.

A browser will open. Connect your Github repository by selecting the Connect App button, choosing Github, authorizing Github, and selecting your forked repository.

Select the master branch.

Create a new service role if needed. Use the defaults.

Create a new environment named “master”.

On the next page deploy the application and watch the build process.

Back in the terminal, hit Enter to end the command prompt. You can visit the link on the build page when the app is done to view the application!

One of the advantages of using the Amplify-CLI and Amplify Console is having the ability to run builds when new code is pushed to the Github repository. This deployment requires a little understanding of AWS, but in the end there are benefits to utilizing their services.

To not incur usage charges, delete resources and resource groups on AWS.

6. How to deploy a React App on Azure

Azure has static hosting capabilities and web application capabilities (as well as many other cloud services). Recently, they added a Static Web App resource that is geared towards frontend frameworks like React, Angular, Vue, and static publishing frameworks like Gatsby and Next. Furthermore, the resource can be integrated with Github to make the workflow easier.

It truly is easier if you are using VSCode to set up your Azure projects. They have many plugins that can save you time. Therefore, this deployment requires that you have;

Follow the forking instructions above.

1. Configure an Azure Function in Project

Open up the forked project in Visual Studio Code.

Create a new empty folder at the root of the project named api/.

Open the command palette (press F1) and type in Azure Functions: Create New Project

Next, click Browse in the dropdown and select the empty api folder.

You will be asked to choose a language. Select Javascript.

Then, select HTTPTrigger for the template and name the function “weather”.

In this example, we will deploy a web application.

You will need to have an account with Azure. You can sign up here.

Next, for this example, select Anonymous access.

Finally, add a route declaration so we can easily call the function. Open up api/weather/function.json and add,

"route":"weather"

under methods. The file should now read;

{
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ],
      "route": "weather"   // new
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}

2. Add API Function

In an open terminal, change directories into api. Then install Axios with npm install axios --save.

Next, remove the code inside of api/weather/index.js and replace it with the code below.

const axios = require('axios')

module.exports = async function (context, req) {

    if (req.method !== "POST") {
        context.res = {
            status: 405,
            body: 'Method not allowed'
        }
    }

    const { location } = req.body

    const encodedLocation = encodeURIComponent(location)

    try {
        // Call the Weather API
        const { data } = await axios({
            method: "GET",
            url: `https://aerisweather1.p.rapidapi.com/observations/${encodedLocation}`,
            headers: {
                "content-type": "application/octet-stream",
                "x-rapidapi-host": "aerisweather1.p.rapidapi.com",
                "x-rapidapi-key": process.env["RAPIDAPI_KEY"],
                "useQueryString": true
            }
        })

        // Pull the information that we need from the Weather API response
        const weatherData = {
            conditions: data.response.ob.weather,
            tempC: data.response.ob.tempC,
            tempF: data.response.ob.tempF
        }
        // Return the data object
        context.res = {
            body: weatherData
        }
    } catch (e) {
        console.log(e)

        context.res = {
            status: 500,
            body: "An error occurred"
        }
    }
}

Azure uses a context object to return the res object. Most aspects are pretty standard in terms of making an API call to the Aeris Weather API and returning the data. Also, there are comments in the code that detail what the function is doing

3. Deploy Static Web App on Azure

Push your changes to the forked Git repository.

Navigate to your Azure portal and create a new resource.

Search for Static Web App

Select Create.

You will need to make a new resource group, pick a name, select a resource region, and authorize Azure to access your Github account.

Then select your account and the repository name for the forked repository. Click on the master branch.

Before creating the resource click on Next: Build. Enter the values in the image to tell the static web app where to find certain resources. Make sure your values match the ones in the image.

Finally, click Review + Create. The resource is created and a build is initiated in your repository on Github. You can view the build process by going to the resource on Azure and clicking on the popup link (“Thank you for using Azure Static Web App! We have not received any content for your site yet. Click here to check the status of your GitHub Action runs.”).

If you follow the link you can see when your Github action is completed. Next, go back to the resource page and click on the URL link in the dashboard.

The browser will open displaying our weather app, but we haven’t added the API key to the environment yet.

4. Add API Key

On the static app resource page (same page where the URL is found) click on Configuration in the sidebar. Then select Add to add a new value.

The value name needs to add the name that we used in the app for deployment (“RAPIDAPI_KEY”).

Insert your API key into the value field. After hitting “OK”, click Save at the top of the Configuration pane.

Head back over to the app and test out a location.

The environment variable should now be loaded and weather data can be fetched!

To not incur usage charges, delete resources and resource groups on the Azure portal.

7. How to deploy a React App on Netlify

Netlify makes things easier. With Netlify, we can configure serverless functions in our project that Netlify converts to AWS Lambda functions. That means that the serverless function set-up is simplified and we can make an easy call to a serverless route for our weather app data.

There is a bit of configuration that we need to take care of first, but I think you will agree that there are clear benefits to this process.

Sign up for an account on Netlify.

Follow the forking instructions above.

Open up the project with your text editor.

Remember, we don’t currently have an API call in place because we can’t secure our API key on the front end. Therefore, we need to implement an API call.

1. Add Call to Weather API

In the project, root create the folder functions and inside of the folder add the file weather.js.

Add the below code to weather.js.

const axios = require('axios');

exports.handler = async function (event, context) {
    let RAPIDAPI_KEY;

    // Access API key from environment
    RAPIDAPI_KEY = process.env.RAPIDAPI_KEY

    // Validate HTTP request type
    if (event.httpMethod !== "POST") {
        return {
            statusCode: 405,
            body: "Method Not Allowed"
        };
    }

    // Get the body of the request
    const body = JSON.parse(event.body)

    // Assign the location value to the variable location from the body object
    const { location } = body

    // Encode the variable so we can send the location in a URL
    const encodedLocation = encodeURIComponent(location)

    try {
        // Call the Weather API
        const { data } = await axios({
            method: "GET",
            url: `https://aerisweather1.p.rapidapi.com/observations/${encodedLocation}`,
            headers: {
                "content-type": "application/octet-stream",
                "x-rapidapi-host": "aerisweather1.p.rapidapi.com",
                "x-rapidapi-key": RAPIDAPI_KEY,
                "useQueryString": true
            }
        })

        // Pull the information that we need from the Weather API response
        const weatherData = {
            conditions: data.response.ob.weather,
            tempC: data.response.ob.tempC,
            tempF: data.response.ob.tempF
        }

        // Return the data object
        return {
            statusCode: 200,
            body: JSON.stringify(weatherData)
        }
    } catch (e) {
        console.log(e)
        return {
            statusCode: 500,
            body: 'Server error.'
        }
    }
}

I left comments in the code to explain what is happening in the function. You can read more about Netlify functions on their documentation page.

Next, on line 18 of App.js, change the URL to /.netlify/functions/weather

2. Add netlify.toml file

This is a configuration file that Netlify needs so it can turn your function files into serverless functions.

Create the netlify.toml file at the project root and add the code below.

[build]
  functions = "functions"

3. Deploy to Netlify

Push the changes to the repository.

$ git add netlify.toml functions/weather.js src/App.js
$ git commit -m "add serverless function"
$ git push

Log in to Netlify.

Click the button on the page that reads New Site from Git.

You will have to authorize Netlify to access your Github repositories.

After authorization, there will be a list of your Github repositories. Select the one that we just created.

On the next page, it will ask for a build step and publish directory. We do not have a build step so leave that blank.

Add public to the publish directory input. Deploy the site.

It will build quickly and there will be a link for your new site!

If you click on the link you should see our app, but it won’t work yet because we didn’t add our API key to the environment variables.

4. Add API Key to Environment Variables

In Site Settings, select the Build & Deploy tab. Scroll towards the bottom and add our key to the environment variables section.

Remember, it needs to equal the value that we placed in the serverless function.

After adding the environment variable you can trigger a new deploy from the Deploys tab.

The application should be functioning at the URL you are provided!

To not incur usage charges, deactivate or delete the site.

Conclusion

The application morphed into almost seven different applications throughout the process. Some aspects change when we factor in how we want to deploy and how to handle things like security. Understanding the end environment can save you time later if you can develop the needed structure ahead of time.

Furthermore, choosing certain deployment services can afford better workflows. Or, you can choose an option that gives you control of the end environment and create your own custom workflow.

The probability that your React app will need to conceal secrets, API-keys, etc. is fairly high. Therefore, you should know some of the options that can offer this protection.

Deploying an app brings along new challenges, but the first time or two tends to be the most difficult. Soon, you develop a better understanding and can quickly set up application deployments that fit your needs. If you have any questions please leave a comment below!

5/5 - (1 vote)
Share
Published by

Recent Posts

Power Up Your Enterprise Hub: New March Release Boosts Admin Capabilities and Streamlines Integrations

We're thrilled to announce the latest update to the Rapid Enterprise API Hub (version 2024.3)!…

1 week ago

Unveiling User Intent: How Search Term Insights Can Empower Your Enterprise API Hub

Are you curious about what your API consumers are searching for? Is your Hub effectively…

1 week ago

Rapid Enterprise API Hub Levels Up Custom Branding, Monetization, and Management in February Release

The RapidAPI team is excited to announce the February 2024 update (version 2024.2) for the…

3 weeks ago

Supercharge Your Enterprise Hub with January’s Release: Search Insights, Login Flexibility, and More!

This January's release brings exciting features and improvements designed to empower you and your developers.…

2 months ago

Enhanced Functionality and Improved User Experience with the Rapid API Enterprise Hub November 2023 Release

Rapid API is committed to providing its users with the best possible experience, and the…

5 months ago

The Power of Supporting Multiple API Gateways in an API Marketplace Platform

In today's fast-paced digital world, APIs (Application Programming Interfaces) have become the backbone of modern…

5 months ago