Deploy React App To Heroku Using Postgres & Express

Heroku is an web application that makes deploying applications easy for a beginner.


Deploy React App To Heroku Using Postgres & Express

Heroku is an web application that makes deploying applications easy for a beginner.

Before you begin deploying, make sure to remove any console.log's or debugger's in any production code. You can search your entire project folder if you are using them anywhere.

You will set up Heroku to run on a production, not development, version of your application. When a Node.js application like yours is pushed up to Heroku, it is identified as a Node.js application because of the package.json file. It runs npm install automatically. Then, if there is a heroku-postbuild script in the package.json file, it will run that script. Afterwards, it will automatically run npm start.

In the following phases, you will configure your application to work in production, not just in development, and configure the package.json scripts for install, heroku-postbuild and start scripts to install, build your React application, and start the Express production server.

Phase 1: Heroku Connection

If you haven't created a Heroku account yet, create one here.

Add a new application in your Heroku dashboard named whatever you want. Under the "Resources" tab in your new application, click "Find more add-ons" and add the "Heroku Postgres" add-on with the free Hobby Dev setting.

In your terminal, install the Heroku CLI. Afterwards, login to Heroku in your terminal by running the following:

heroku login

Add Heroku as a remote to your project's git repository in the following command and replace <name-of-Heroku-app> with the name of the application you created in the Heroku dashboard.

heroku git:remote -a <name-of-Heroku-app>

Next, you will set up your Express + React application to be deployable to Heroku.

Phase 2: Setting up your Express + React application

Right now, your React application is on a different localhost port than your Express application. However, since your React application only consists of static files that don't need to bundled continuously with changes in production, your Express application can serve the React assets in production too. These static files live in the frontend/build folder after running npm run build in the frontend folder.

Add the following changes into your backend/routes.index.js file.

At the root route, serve the React application's static index.html file along with XSRF-TOKEN cookie. Then serve up all the React application's static files using the express.static middleware. Serve the index.html and set the XSRF-TOKEN cookie again on all routes that don't start in /api. You should already have this set up in backend/routes/index.js which should now look like this:

// backend/routes/index.js
const express = require('express');
const router = express.Router();
const apiRouter = require('./api');

router.use('/api', apiRouter);

// Static routes
// Serve React build files in production
if (process.env.NODE_ENV === 'production') {
  const path = require('path');
  // Serve the frontend's index.html file at the root route
  router.get('/', (req, res) => {
    res.cookie('XSRF-TOKEN', req.csrfToken());
    res.sendFile(
      path.resolve(__dirname, '../../frontend', 'build', 'index.html')
    );
  });

  // Serve the static assets in the frontend's build folder
  router.use(express.static(path.resolve("../frontend/build")));

  // Serve the frontend's index.html file at all other routes NOT starting with /api
  router.get(/^(?!\/?api).*/, (req, res) => {
    res.cookie('XSRF-TOKEN', req.csrfToken());
    res.sendFile(
      path.resolve(__dirname, '../../frontend', 'build', 'index.html')
    );
  });
}

// Add a XSRF-TOKEN cookie in development
if (process.env.NODE_ENV !== 'production') {
  router.get('/api/csrf/restore', (req, res) => {
    res.cookie('XSRF-TOKEN', req.csrfToken());
    res.status(201).json({});
  });
}

module.exports = router;

Your Express backend's package.json should include scripts to run the sequelize CLI commands.

The backend/package.json's scripts should now look like this:

"scripts": {
    "sequelize": "sequelize",
    "sequelize-cli": "sequelize-cli",
    "start": "per-env",
    "start:development": "nodemon -r dotenv/config ./bin/www",
    "start:production": "node ./bin/www"
  },

Initialize a package.json file at the very root of your project directory (outside of both the backend and frontend folders). The scripts defined in this package.json file will be run by Heroku, not the scripts defined in the backend/package.json or the frontend/package.json.

When Heroku runs npm install, it should install packages for both the backend and the frontend. Overwrite the install script in the root package.json with:

npm --prefix backend install backend && npm --prefix frontend install frontend

This will run npm install in the backend folder then run npm install in the frontend folder.

Next, define a heroku-postbuild script that will run the npm run build command in the frontend folder. Remember, Heroku will automatically run this script after running npm install.

Define a sequelize script that will run npm run sequelize in the backend folder.

Finally, define a start that will run npm start in the `backend folder.

The root package.json's scripts should look like this:

"scripts": {
    "heroku-postbuild": "npm run build --prefix frontend",
    "install": "npm --prefix backend install backend && npm --prefix frontend install frontend",
    "dev:backend": "npm install --prefix backend start",
    "dev:frontend": "npm install --prefix frontend start",
    "sequelize": "npm run --prefix backend sequelize",
    "sequelize-cli": "npm run --prefix backend sequelize-cli",
    "start": "npm start --prefix backend"
  },

The dev:backend and dev:frontend scripts are optional and will not be used for Heroku.

Finally, commit your changes.

Phase 3: Deploy to Heroku

Once you're finished setting this up, navigate to your application's Heroku dashboard. Under "Settings" there is a section for "Config Vars". Click the Reveal Config Vars button to see all your production environment variables. You should have a DATABASE_URL environment variable already from the Heroku Postgres add-on.

Add environment variables for JWT_EXPIRES_IN and JWT_SECRET and any other environment variables you need for production.

You can also set environment variables through the Heroku CLI you installed earlier in your terminal. See the docs for Setting Heroku Config Variables.

Push your project to Heroku. Heroku only allows the master branch to be pushed. But, you can alias your branch to be named master when pushing to Heroku. For example, to push a branch called login-branch to master run:

git push heroku login-branch:master

If you do want to push the master branch, just run:

git push heroku master

You may want to make two applications on Heroku, the master branch site that should have working code only. And your staging site that you can use to test your work in progress code.

Now you need to migrate and seed your production database.

Using the Heroku CLI, you can run commands inside of your production application just like in development using the heroku run command.

For example to migrate the production database, run:

heroku run npm run sequelize db:migrate

To seed the production database, run:

heroku run npm run sequelize db:seed:all

Note: You can interact with your database this way as you'd like, but beware that db:drop cannot be run in the Heroku environment. If you want to drop and create the database, you need to remove and add back the "Heroku Postgres" add-on.

Another way to interact with the production application is by opening a bash shell through your terminal by running:

heroku bash

In the opened shell, you can run things like npm run sequelize db:migrate.

Open your deployed site and check to see if you successfully deployed your Express + React application to Heroku!

If you see an Application Error or are experiencing different behavior than what you see in your local environment, check the logs by running:

heroku logs

If you want to open a connection to the logs to continuously output to your terminal, then run:

heroku logs --tail

The logs may clue you into why you are experiencing errors or different behavior.

If you found this guide helpful feel free to checkout my github/gists where I host