Deploy Backend And Frontend Apps On The Same Heroku Dyno
By: Chris I.
Tags: Coding, Software Development, Heroku, Ruby on Rails, React

Deploy Backend And Frontend Apps On The Same Heroku Dyno

You can run 2 apps (frontend and backend) on the same Heroku dyno.

It’s just not straightforward… There’s a tonne of ways it won’t work, and only 1 that will.

Today we’ll walk though setting up a ReactJS/Node frontend and a Rails API backend.

My final implementation was inspired by this post on Heroku’s blog. But it’s quite long and meandering.

We’ll just walk through the meat and potatoes of get it working. No fluff.

Create and setup the app

Run these on the command line.

$ rails new fe-be-app --api --database=postgresql -T

$ cd fe-be-app

$ bundle install

$ rake db:create

$ rails s -p 8000

Now visit the localhost:8000 to confirm you see the “Yay! You’re on Rails!” page. Great, working so far!

Add these lines to .gitignore

.idea/

tmp/

log/

No reason to push unnecessary code to Heroku.

Add the frontend

Run these on the command line.

$ npx create-react-app client

$ cd client

$ npm install axios

$ npm install http-proxy-middleware

And copy/paste this into client/src/App.js .

import React, {useState, useEffect} from 'react';

import axios from 'axios'

import './App.css';



const App = () => {

useEffect(() => {

axios.get('/api/bears')

.then(res => setBears(res.data))

.catch(error => console.log(error))

}, []);



const [bears, setBears] = useState([]);



return (

<div className="App">

<h2>Bears in Canada:</h2>

<div>

<ul>

{bears.map((b,idx) => {

return (

<li>

{idx}. {b}

</li>

)

})}

</ul>

</div>

</div>

);

}



export default App;

Now test that the frontend works in isolation.

$ yarn --cwd client start

Do you see “Bears in Canada:” at the top? Perfect!

Setup proxying requests to the backend

Add a file called client/src/setupProxy.js with the following code.

const { createProxyMiddleware } = require('http-proxy-middleware');



module.exports = function(app) {

app.use(

'/api',

createProxyMiddleware({

target: 'http://localhost:8000',

changeOrigin: true,

})

);

};

This saves you from providing the whole URL in every frontend request, as well as different URLs for both development and production.

Add the Rails controller and route

Create a controller at controllers/api/bears_controller.rb with this code.

class Api::BearsController < ApplicationController

BEARS = [

'Polar Bear',

'Grizzly Bear',

'Black Bear',

'Brown Bear',

'Kermode Bear',

'Giant Panda',

'Sloth Bear']



def index

render json: BEARS, status: 200

end

end

We’ll simply return a list of bear names to the frontend. Keep it simple!

Now update routes.rb with a single route.

Rails.application.routes.draw do

namespace :api do

get '/bears', to: 'bears#index'

end

end

Add the Procfile

This file is called Procfile . It exists in the root of the app, and has no file extension. It tells the Heroku dyno what to do upon booting.

web: bundle exec rails s

release: bin/rake db:migrate

Add package.json in the root

Create this file and paste in the following.

{

"name": "frontend-backend-app",

"license": "MIT",

"engines": {

"node": "14.8.0",

"yarn": "1.22.4"

},

"scripts": {

"build": "yarn --cwd client install && yarn --cwd client build",

"deploy": "cp -a client/build/. public/",

"heroku-postbuild": "yarn build && yarn deploy"

}

}

This is in addition to the other package.json in your frontend app.

Add buildpacks

On the command line, run.

$ heroku buildpacks:add heroku/nodejs --index 1

$ heroku buildpacks:add heroku/ruby --index 2

These tell Heroku what to install on the server to support your code.

Deploy to Heroku

$ git add . -A

$ git commit -m 'first commit'

$ heroku create

$ git push heroku master

When you visit your Heroku link, you should see a page listing the bear names we specified in our controller.

Booya!

Conclusion

I tried and failed 10 different ways before reaching this solution that works. So I hopes this saves you that time and headache :)

Drop a comment if you get stuck and I’ll do my best to help!

Happy coding!