React Production Deployment with Heroku

How to deploy a React app with a back-end API. Part 3: Heroku.

Esau Silva
Bits and Pieces

--

Photo by Jakob Owens on Unsplash

In this tutorial, we will learn how to deploy a React app with a back-end API to Heroku. We will cover deploying an Express.js API to interact with the React front-end.

This is part 3 of the tutorial. In part 1 we learned how to deploy the app to Netlify including deploying Lambda Functions, you can read it here. In part 2 of the tutorial we learned how to deploy the app to Vercel, you can read it here.

If you already read part 1 or 2 of the tutorial, you can skip this section and jump right to Heroku section.

Tip: Use Bit to share and install React components. Use your components to build new apps, and share them with your team to build faster. Give it a try.

React spinners: choose, play, install

Let’s get started

Our React app will be very basic in nature, it calls an Express.js API with only one POST route and one GET route.

The GET route returns “Hello from Express” and the POST route is just one field that when posting, it returns “I received your POST request. This is what you sent me: [from input]”.

Below is an extract from the Express.js code showing both routes.

app.get('/api/hello', (req, res) => {
res.send({ express: 'Hello From Express' });
});
app.post('/api/world', (req, res) => {
res.send(
`I received your POST request. This is what you sent me: ${req.body.post}`,
);
});

The app also has client side routing with React Router that navigates to a secondary page and displays a 404 page when we enter a URL that does not exist.

Below you can see the demo app.

Demo app

Upon loading, React calls the Express.js GET endpoint and displays a message. When submitting the single field form, React calls the Express.js POST endpoint and displays a message.

I decided to include client side routing because Single Page Applications (SPAs) utilize the history API and when deployed, we need to include some “extra” steps. (Hint: we need to configure redirects)

Let’s say we deploy the app to demoapp.com. When we hit demoapp.com the app loads. Then we click “Another Page (demoapp.com/another-page)” menu link, and the page loads fine; but if we refresh the page we receive an error. That is because the server is looking for a file named “another-page”, but that file does not exist since our entire app (including routing) is being loaded from index.html. So somehow we need to tell the server to redirect all of the requests to index.html.

I will point that exact step in each of the deployments to correct this “issue”.

Basic App Structure

Each deployment has its own root directory. Then we will have our Express.js API under an api directory and the React app either under a clientdirectory or right under the root directory of each project.

Structure

The React app was bootstrapped with Create React App (CRA), so the directory structure is pretty standard with the source files under the srcdirectory and public assets under the public directory.

Heroku

Take a look at the repo under the heroku directory.

Heroku is a little different than Now and Netlify. Here you have to run your React app by configuring your server technology of preference. Heroku supports Node, Ruby, Python, and PHP, among others.

In our case we will create a Node instance and have Express.js serve our React app and the API. Heroku will also build the React app on their servers when you deploy.

Before starting, head over to Heroku and create an account or log in. You will also need to install their CLI tool.

To install the Heroku CLI tool, type the following in your terminal (macOS)

brew tap heroku/brew && brew install heroku

You can also download the installer. For Windows users, you would need to download the Windows installer. You can find out more about it in the installation instructions.

To log in, in your terminal type

heroku login

It will open a Browser window where you would need to log in with your credentials. After that, the CLI will log you in automatically.

Heroku Login Flow

Project Structure

Take a look at the repo under the Heroku directory.

├── /client
└── server.js

Here we have the Express.js API right under the root directory, and the React app under the client directory.

Required Configurations

Just like the previous example, here we also have the Express.js app running on port 5000 and the React app on port 3000. So we would need to setup the proxy server.

This time, the client side package.json is located under the client directory, and we add the proxy like so

"proxy": "http://localhost:5000/",

Now, let’s open the package.json from the Express.js side (under root directory) and notice we have a special npm script

"heroku-postbuild": "cd client && npm install && npm install --only=dev --no-shrinkwrap && npm run build"

This script tells Heroku what to do after deploying the Express.js app. In our case we want to build our React app for production, which will be served by our Express.js app. You can read more about Heroku’s Node support in their docs.

Express.js

Let’s take a look at the Express.js code

const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const app = express();
const port = process.env.PORT || 5000;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// API calls
app.get('/api/hello', (req, res) => {
res.send({ express: 'Hello From Express' });
});
app.post('/api/world', (req, res) => {
res.send(
`I received your POST request. This is what you sent me: ${req.body.post}`,
);
});
if (process.env.NODE_ENV === 'production') {
// Serve any static files
app.use(express.static(path.join(__dirname, 'client/build')));
// Handle React routing, return all requests to React app
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'client/build', 'index.html'));
});
}
app.listen(port, () => console.log(`Listening on port ${port}`));

Everything is the same as the previous example, with the exception of the bolded code. Here we are specifying if NODE_ENV environment variable is equal to production, then we will run some code. Basically we are serving the production ready React app and re-routing client-side routes to index.html.

The reason we do it this way is because when deployed, Heroku will automatically assign production to NODE_ENV environment variable and we only want to run that specific code in production. During local development, React has its own development server, so there is no need for our Express.js app to serve it.

React App

This is how we call the API in our React app

...
callApi = async () => {
const response = await fetch('/api/hello');
const body = await response.json();
if (response.status !== 200) throw Error(body.message);return body;
};
handleSubmit = async e => {
e.preventDefault();
const response = await fetch('/api/world', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ post: this.state.post }),
});
const body = await response.text();
this.setState({ responseToPost: body });
};
...

Deploying to Heroku

To deploy to Heroku, the project would need to be in a local Git repository.
Below are the steps to initialize a new Git repo.

cd my-app-directory/
git init

Then you will need to create an app instance in Heroku. To do so, type the following in your terminal (make sure you are at the root of the project)

heroku create [app-name]

app-name would be the name of the application.

Creating new Heroku app

If you notice in the screenshot, I attempted to create a project with the name create-react-app-express but the name was already taken. When you create apps, the name should be unique across the board. After I chose an alternate name (create-react-app-expressjs), I was able to create the app. Heroku gave me the link to the app and add a git remote.

Heroku git remote

In the above screenshot I’m inspecting the git configuration, and we can see a remote named heroku. This is where we are going to push our code to deploy.

Before deploying, we need to commit the code

git add .
git commit -m "initial commit"

Now we can deploy to Heroku

git push heroku master
Heroku deploy

More about deploying in their docs.

And we have the live app here: https://create-react-app-expressjs.herokuapp.com/

Bonus

Heroku also supports continuous delivery, and they have several options. Take a look at their docs. Another thing about Heroku is that you can install a plethora of Add-ons, such as for sending emails and authentication.

Conclusion

In this article we learned how to deploy a React app with an Express.js API to Heroku. If you also read part 1 and part 2 of the series, now you have the tools to deploy, not only to Now and Heroku, but to Netlify as well.

And like I previously said, by learning how to deploy to multiple providers, you will be able to make an informed decision when it is time to deploy your project to production. Thanks for reading this far. I hope you enjoyed! Feel free to comment below and ask anything, I’d be happy to help :) Cheers!

--

--

Full Stack Software Engineer and avid Brazilian Jiu-Jitsu practitioner