How to Deploy an Angular 2/Rails 5 App to Heroku

Here’s how to deploy an Angular 2 and Rails 5 app to Heroku. There’s more than one possible way this sort of thing could be structured. Before I describe how to set up the deployment I’ll explain how I chose to structure my app and why.

First, my app is a single-page application where the front-end code is completely separate from the back-end API code. The alternative would be to use Rails views and include the JavaScript and CSS in the asset pipeline. I don’t think either way is necessarily better than the other. In this case I chose the SPA way because a) I understand a lot of people are structuring their Angular/Rails apps this way and b) between the two structures, it’s probably the trickier of the two to deploy, and so it’s probably the one that could benefit most from being documented.

I could have chosen to keep my Angular app and my Rails app in two separate repos. In that case the Rails code could have been deployed to Heroku and the Angular code could have been deployed to, say, a static AWS site. What I instead chose to do was to include my Angular app in a subdirectory of the Rails project and host it all on the same Heroku app. The reason I did it this way is because a) front-end and back-end changes are often made at the same time by the same people and b) although it might have been less work initially to host the Angular app and the Rails app in two different places, it’s arguably simpler from a day-to-day workflow standpoint just to have a single way to deploy everything to one place.

The deployment steps below begin with the creation of an Angular/Rails app, so you don’t have to worry about having your own app to deploy. These steps will get you all the way there but you can also clone my example repo if you don’t want to follow the steps manually.

Initializing the App

The first step will be to initialize a new Rails 5 project. I’m calling mine rails_5_angular_2_deployment_example.

For the front-end we’ll use angular2-seed. Clone the repo into a subdirectory called client. (The name client, in this case, is important. If you call it something else, the deployment won’t work.)

We’re just interested in the angular2-seed code. If we keep it as a repository inside our project’s repository, there will be problems. Kill client/.git.

Now install the Node packages.

Before we try to deploy our app we’ll want to make sure it works locally. Run the following command to do a build.

This command will create a directory called client/dist/prod. If we want Rails to be able to serve something out of there, we’ll have to tell Rails about it somehow. We can do this by simply symlinking public/ to client/dist/prod/. Rails will look for public/index.html and find client/dist/prod/index.html and we’ll be in business.

Start the Rails server. If you navigate to localhost:3000 you should see “Howdy! Here’s a list of awesome computer scientists…”

Creating the Heroku App

The first step is somewhat self-explanatory:

We’ll need to use two buildpacks for this deployment. If we were deploying a plain old Rails app, Heroku would auto-detect Ruby and use the heroku/ruby buildpack. We also need a Node buildpack because in order to build our front-end app we need to run a Gulp command and in order to run Gulp commands we need to have Node packages installed.

We can tell Heroku about our two buildpacks like this:

The order matters. If we were to put the Ruby buildpack first and the Node buildpack second, Heroku would give us a single Node dyno which wouldn’t know how to run Ruby. By putting the Ruby buildpack second we get two dynos: a web dyno and a worker dyno, both of which know how to run Ruby.

The reason we’re using my https://github.com/jasonswett/heroku-buildpack-nodejs buildpack instead of the Heroku version is that I needed to modify the buildpack to look for package.json inside of the client directory instead of at the project root.

We need to do one last thing before we can push our code. Modify client/package.json to look like this:

We’ve done two things here. First, we added gulp build.prod to the postinstall script. This will force Heroku to do a build as part of the deployment process. Second, we move everything from devDependencies to dependencies. Since it’s a production environment, Node won’t pick up the devDependencies, but we need those.

By the way, why not just build locally, commit the generated code, and push that? You could do that and it would work. The reason I didn’t want to is that it’s not a good idea to commit build artifacts to version control. You end up with a bunch of “changed” files every time you do a build, which not only doesn’t make sense but serves as a distraction. I’ve worked on projects before that commit build artifacts to version control and it has been painful.

After all our changes are committed we can do a push:

Now open the app in Heroku.

You should see the same Angular 2 Seed app in the production environment. Obviously, the Angular app isn’t talking to Rails but it is coexisting with it, and that’s the hard part.

Leave a Reply

Your email address will not be published. Required fields are marked *