React Production Deployment with Heroku
By: Esau Silva
Tags: JavaScript, React, Expressjs, Node, Heroku

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!