Deploying Rails API Backend + React Frontend App to Heroku
Tags: rails, react, heroku, webdev
Published: 2020-07-10 00:48:22

In this post, I’m going to write about my journey hosting an app on Heroku for the first time. I'll also be going over the steps on how to deploy, so please feel free to follow along. I struggled a lot, especially with deploying the backend, before I was finally able to get the app up and running, so I'll also talk about what kind of errors I ran into and hopefully, that will help anyone who is experiencing the same issues.

## Table of Contents
- [Preparation Before Deploying](#prep-before-deploy)
- [Setting up Heroku CLI](#setup-cli)
- [Deploying App to Heroku](#deploy-to-heroku)
  - [Step 1: Setting up Rails API Backend](#setup-rails)
  - [Step 2: Deploying Rails API Backend](#deploy-rails)
  - [Errors I Encountered for Rails App and How to Debug](#errors)
  - [Step 3: Deploy React Frontend](#deploy-react)


## Preparation Before Deploying <a name="prep-before-deploy"/>
- I already had my Rails backend and React frontend in two separate GitHub repositories. This is important because the setup is different. If you have your app is set up where both the frontend and backend are combined into one, you can refer to this [video](https://www.youtube.com/watch?v=Z0sFlwz0YvU)
- My Rails API already has PostgreSQL as the database. If you don't have PostgreSQL set up, refer to [Converting Rails from SQLite3 to PostgreSQL]( https://medium.com/@virtual_khan/converting-rails-from-sqlite3-to-postgresql-d97023314a14). For future reference, if you know that you will be creating an app that you plan to deploy to Heroku, run `rails new your-app-name-here --api --database=postgresql` so you don't have to switch from SQLite later.
- I created a Heroku account. If you want to learn more about how the free option of Heroku works, read [How FREE Heroku really works and how to get maximum from it?](https://medium.com/@AndreyAzimov/how-free-heroku-really-works-and-how-to-get-maximum-from-it-daa53f2b3c57)
- I installed Heroku's Command Line Inteface (CLI). This allows you to create and manage your Heroku apps from the terminal. I followed Heroku’s [guide](https://devcenter.heroku.com/articles/heroku-cli#getting-started) to set it up.
- Rails version 6.x
- Ruby version 2.6.1 and above
- Pushed all changes to Git before deploying
- I deployed from the master branch of my project

## Setting up Heroku CLI <a name="setup-cli"/>
Since I use a Mac, I just opened up my terminal and ran 
```
$ brew tap heroku/brew && brew install heroku
```

After you have installed the CLI, in your terminal, run
```
$ heroku login
```
Follow the prompts to log in to your Heroku account.

## Deploying App to Heroku <a name="deploy-to-heroku"/>

Since I had my frontend and backend on separate repositories, I had to deploy the two separately to Heroku, so I created <mark>two separate Heroku apps</mark>. Using my app as an example, I named one Heroku app, `the-next-episode-api`, for the Rails backend and the other as `the-next-episode` for the React frontend. Note, the names of the apps don't have to match what you have for your GitHub repos.

### Step 1: Setting up Rails API Backend <a name="setup-rails"/>
Make sure you have a root route/path in your `config/routes.rb` and a static page that can be rendered when the app is first opened before React takes over. I didn't have one the first time I deployed my backend and I got a 404 error when I opened the Heroku app in the browser. 

Heroku's [guide](https://devcenter.heroku.com/articles/getting-started-with-rails6#create-a-welcome-page) goes over how to create a welcome page. The guide also explains that "Rails 6 no longer has a static index page in production by default. When you’re using a new app, there will not be a root page in production, so we need to create one." 

From the Heroku guide:
We will first create a controller called `welcome` for our home page to live:
```
rails generate controller welcome
```

Next we’ll add an index page:
In file `app/views/welcome/index.html.erb` write:
```
<h2>Welcome to ...</h2> 
```
Now we need to make Rails route to this action. We’ll edit `config/routes.rb` to set the index page to our new method:

In file `config/routes.rb`, on line 2 add:
```
root 'welcome#index'
```
You can verify that the page is there by running your server:
```
rails server
```
And visiting http://localhost:3000 in your browser. If you do not see the page, use the logs that are output to your server to debug.

### Step 2: Deploying Rails API Backend <a name="deploy-rails"/>
1. **Navigate into the directory of your Rails backend** ```
cd the-next-episode-api
```

2. **Create Heroku app** ```
heroku create the-next-episode-api
```

3. **Initialize Heroku git remote** ```
git remote add heroku git@heroku.com:the-next-episode-api.git
```

4. **Add, commit, and push to your Heroku remote** ```
git add .
git commit -m "first Heroku deploy"
git push heroku master
```

5. **Migrate your database (don't need to run `heroku run rake db:create`)** ```
heroku run rake db:migrate
```

6. **(only if you need to seed database)** ```
heroku run rake db:seed
```
7. If you have any keys you need to keep secret, like for JWT or external API, Heroku does not recognize ENV variables, so you need to configure those variables. There are two ways to do so:<br/>

**Method 1**: Can do so in Heroku CLI 
```
heroku config:set API_KEY=put-your-api-key-here
```

Confirm variable is set, by running 
```
heroku config
GITHUB_USERNAME: joesmith
API_KEY:   your-api-key-here

OR

heroku config:get API_KEY
your-api-key-here
```

**Method 2**: Go to your app's settings on heroku.com and scroll down to Config Vars and add it there. <br />


### Errors I Encountered for Rails App and How to Debug <a name="errors"/>

**404 Error (page can't be found)** <br />
Solution: add root route to `config/routes`

I mentioned this before in the Setting up Rails API Backend section, but to fix this, I had to figure out why I was getting this error. To get a more detailed look at the error, I ran `heroku logs` in the terminal and scrolled up until I saw an error that said `ActionController::RoutingError (No route matches [GET] "/")`. 

So after some research, I realized I was getting this error because I did not have a root route.

**500 Error** <br /> 
Solution: Fix a couple of methods in my `application_controller.rb`

I was getting a 500 error because of the authorization/authentication methods I had in my application_controller. The methods were looking for a JSON Web Token or `current_user`, but logging in and creating a user shouldn't require a JSON Web Token. That wouldn't make sense, so I added a check to check if a token exists (shouldn't exist if user is trying to log in/sign up),
```
def user_id
  if decoded_token
    decoded_token[0]["user_id"]
  end
end
```

After updating these methods, I used Postman to check all my endpoints to make sure they are working like they're supposed to. Now, I was able to create a new user and log in without needing a token. I also checked the other endpoints that didn't need authorization and ones that did. They all worked!

So, I added, committed, pushed (`git push heroku master`) the changes, and opened the Heroku app again. I went to Postman and replaced `http://localhost:3000/api/v1/shows` with `https://the-next-episode-api.herokuapp.com/api/v1/shows` and I got my JSON data. I checked all my other endpoints too just in case and everything worked, so I moved onto deploying my React frontend.

### Step 3: Deploy React Frontend <a name="deploy-react"/>

**1. If using React Router, create file in project's *root directory* named `static.json` that has the following code:**
```
{ "root": "build/", "routes": { "/**": "index.html" } }
```
In this [stack overflow post](https://stackoverflow.com/questions/41772411/react-routing-works-in-local-machine-but-not-heroku), the second answer explains why we need this file, <br/> 

"The public static directory is mapped to the / endpoint, and visiting this endpoint from a browser will download the index.html webpage. This webpage in turn loads the React components. And because React Browser Router is a React component, the routes are loaded dynamically after visiting the / endpoint. In other words, before the index.html webpage is loaded all our React Browser Router routes will result in 404 errors on Heroku. To resolve this issue, a static.json file can be used to map any endpoints with the following pattern /** to the index.html file, which in turn will load React Browser Router and correctly load the react components for that route."

**2. Update all the URLs in your fetch requests or any other reference to your Rails API**

Replace all `http://localhost:3000` with the URL of your deployed backend, `https://your-app-name-here-api.herokuapp.com`. 

<mark>If you have your routes namespaced</mark>, like `http://localhost:3000/api/v1/...`, replace ONLY the `http://localhost:3000` part. <mark>Leave this part `/api/v1/...`.</mark> So, it should look something like <br />
 `https://your-app-name-here-api.herokuapp.com/api/v1/...`
<br/>


**3. Create Heroku app set to `create-react-app` buildpack:**
```
heroku create your-app-name-here --buildpack https://github.com/mars/create-react-app-buildpack.git
```
Without the buildpack, Heroku will deploy the development build (which is slow) of your React app instead of the optimized production build.

**4. Initialize Heroku remote:**
```
git remote add heroku git@heroku.com:your-app-name-here.git
``` 

**5. Add, commit, and push to Heroku remote:**
```
git add .
git commit -m "First Heroku deploy with create-react-app"
git push heroku master
```

**6. Open app and you're done!**
```
heroku open
```

This is my app, [The Next Episode](https://the-next-episode.herokuapp.com/) if you want to check it out.

Thanks for reading and hope this helped!