How to Deploy a Rails Application with an Angular CLI Webpack Front-End

by Jason Swett,

I’ve written previously about how to deploy an Angular CLI Webpack project without Rails.

I’ve also written about how to deploy an Angular 2/Rails 5 project, but not one that uses Angular CLI Webpack.

Following are instructions for deploying a Rails 5 app with an Angular 2 front-end that was generated with Angular CLI, Webpack version. (Specifically, Angular CLI version 1.0.0-beta.11-webpack.8.)

Create the Rails App

First, create an API-only Rails application.

Create the Angular App

Before you create the Angular app, make sure you have the following versions of the following things installed:

Angular CLI: 1.0.0-beta.11-webpack.8
NPM: 3.10.6
Node: 6.5.0

Then, just like I had us do in the Angular-only version of this post, we’ll create an Angular app with the silly name of bananas.

In this case it’s important that we call the Angular directory client. Don’t worry about why right now. I’ll explain shortly.

$ ng new bananas
$ mv bananas client

And also just like in the Angular-only version, we’ll want to make sure we can run ng-build without problems.

$ cd client
$ ng build

If you try it and it doesn’t work (which is very likely), just refer to the other post for how to fix it.

Modify package.json

We need to do a few things to package.json. Don’t worry if you don’t understand every item. I’ve shared my package.json below which you can just copy and paste if you want.

We want Heroku to do an ng build for us after the build. So we need to add this line:

"heroku-postbuild": "ng build",

We also need to move our devDependencies into dependencies because we need some of them in production.

We’ll want to remove the "start": "ng serve" script because it doesn’t apply.

Lastly, an absence of node-gyp will cause error messages to appear. So we’ll add this line:

"preinstall": "npm install -g node-gyp",

Following is what my package.json looks like.

{
  "name": "bananas",
  "version": "0.0.0",
  "license": "MIT",
  "angular-cli": {},
  "scripts": {
    "lint": "tslint \"src/**/*.ts\"",
    "test": "ng test",
    "pree2e": "webdriver-manager update",
    "e2e": "protractor",
    "preinstall": "npm install -g node-gyp",
    "heroku-postbuild": "ng build"
  },
  "private": true,
  "dependencies": {
    "@angular/common": "2.0.0-rc.5",
    "@angular/compiler": "2.0.0-rc.5",
    "@angular/core": "2.0.0-rc.5",
    "@angular/forms": "0.3.0",
    "@angular/http": "2.0.0-rc.5",
    "@angular/platform-browser": "2.0.0-rc.5",
    "@angular/platform-browser-dynamic": "2.0.0-rc.5",
    "@angular/router": "3.0.0-rc.1",
    "core-js": "^2.4.0",
    "rxjs": "5.0.0-beta.11",
    "ts-helpers": "^1.1.1",
    "zone.js": "0.6.12",
    "@types/jasmine": "^2.2.30",
    "angular-cli": "1.0.0-beta.11-webpack.8",
    "codelyzer": "~0.0.26",
    "jasmine-core": "2.4.1",
    "jasmine-spec-reporter": "2.5.0",
    "karma": "0.13.22",
    "karma-chrome-launcher": "0.2.3",
    "karma-jasmine": "0.3.8",
    "karma-remap-istanbul": "^0.2.1",
    "protractor": "4.0.3",
    "ts-node": "1.2.1",
    "tslint": "3.13.0",
    "typescript": "2.0.0"
  },
  "devDependencies": {
  }
}

Create public symlink

When someone visits /index.html in the browser, the file they’ll actually be served is Rails’ public/index.html. We can do a little trick where we symlink the public directory to client/dist. That way when someone visits /index.html they’ll be served client/dist/index.html, thus loading the Angular app.

Let’s kill the public directory and replace it with a symlink.

$ rm -rf public
$ ln -s client/dist public

Create the Heroku app

I’m assuming you have a Heroku account and you have Heroku CLI installed.

$ heroku create

Specify the buildpacks

Lastly, we’ll tell Heroku to use two certain buildpacks:

$ heroku buildpacks:add https://github.com/jasonswett/heroku-buildpack-nodejs
$ heroku buildpacks:add heroku/ruby

Remember when I said we needed to call our Angular directory client? The reason is because of my custom Node buildpack. I’ve modified Heroku’s Node buildpack to look for package.json in client rather than at the root. This is what allows us to nest an Angular app inside of a Rails app and still have Heroku do what it needs to do with each.

Deploy the app

Make sure your code is all committed and do a git push.

$ git push heroku master

When it’s done, open the app. You should see “app works!”.

$ heroku open

If you want, I have a full repo with the code used in this example.

4 thoughts on “How to Deploy a Rails Application with an Angular CLI Webpack Front-End

  1. genesse

    Can you explain the changes you needed to make to package.json? I’m doing this tutorial in November 2018, so the dependencies are much later versions than when you wrote it. I’d prefer to just tweak them as opposed to copy/pasting the entire thing. Thanks!

    Reply
  2. genesse

    Hi Jason. Please disregard my recent question about package.json. I got that part figured out. But I do have another question now. I deployed to Heroku but am getting a 404 when I try to open the site. “No webpage was found for the web address” How can I trouble shoot this? Thanks.

    Reply
  3. genesse

    Last comment of the night, I promise! Looking at the Heroku logs, it looks like it’s a Rails routing issue. I wouldn’t expect this since I thought it was the index.html via the symlink public file that was supposed to load. Any ideas?

    2018-11-09T02:22:01.787689+00:00 app[web.1]: [8c0a5376-0d7f-4c4e-b1f6-16a1a23c8a3d] vendor/bundle/ruby/2.4.0/gems/puma-3.12.0/lib/puma/thread_pool.rb:133:in `block in spawn_thread’
    2018-11-09T02:22:02.368025+00:00 app[web.1]: I, [2018-11-09T02:22:02.367812 #4] INFO — : [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] Started GET “/” for 71.56.81.32 at 2018-11-09 02:22:02 +0000
    2018-11-09T02:22:02.381671+00:00 app[web.1]: F, [2018-11-09T02:22:02.381553 #4] FATAL — : [6fb4909a-cda5-4c17-ab8e-f62b661af2cd]
    2018-11-09T02:22:02.381747+00:00 app[web.1]: F, [2018-11-09T02:22:02.381660 #4] FATAL — : [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] ActionController::RoutingError (No route matches [GET] “/”):
    2018-11-09T02:22:02.381802+00:00 app[web.1]: F, [2018-11-09T02:22:02.381744 #4] FATAL — : [6fb4909a-cda5-4c17-ab8e-f62b661af2cd]
    2018-11-09T02:22:02.381903+00:00 app[web.1]: F, [2018-11-09T02:22:02.381811 #4] FATAL — : [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/actionpack-5.2.1/lib/action_dispatch/middleware/debug_exceptions.rb:65:in `call’
    2018-11-09T02:22:02.381906+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/actionpack-5.2.1/lib/action_dispatch/middleware/show_exceptions.rb:33:in `call’
    2018-11-09T02:22:02.381908+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/railties-5.2.1/lib/rails/rack/logger.rb:38:in `call_app’
    2018-11-09T02:22:02.381910+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/railties-5.2.1/lib/rails/rack/logger.rb:26:in `block in call’
    2018-11-09T02:22:02.381912+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/activesupport-5.2.1/lib/active_support/tagged_logging.rb:71:in `block in tagged’
    2018-11-09T02:22:02.381914+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/activesupport-5.2.1/lib/active_support/tagged_logging.rb:28:in `tagged’
    2018-11-09T02:22:02.381916+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/activesupport-5.2.1/lib/active_support/tagged_logging.rb:71:in `tagged’
    2018-11-09T02:22:02.381917+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/railties-5.2.1/lib/rails/rack/logger.rb:26:in `call’
    2018-11-09T02:22:02.381919+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/actionpack-5.2.1/lib/action_dispatch/middleware/remote_ip.rb:81:in `call’
    2018-11-09T02:22:02.381920+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/actionpack-5.2.1/lib/action_dispatch/middleware/request_id.rb:27:in `call’
    2018-11-09T02:22:02.381922+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/rack-2.0.6/lib/rack/runtime.rb:22:in `call’
    2018-11-09T02:22:02.381924+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/activesupport-5.2.1/lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call’
    2018-11-09T02:22:02.381926+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/actionpack-5.2.1/lib/action_dispatch/middleware/executor.rb:14:in `call’
    2018-11-09T02:22:02.381928+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/actionpack-5.2.1/lib/action_dispatch/middleware/static.rb:127:in `call’
    2018-11-09T02:22:02.381929+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/rack-2.0.6/lib/rack/sendfile.rb:111:in `call’
    2018-11-09T02:22:02.381931+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/railties-5.2.1/lib/rails/engine.rb:524:in `call’
    2018-11-09T02:22:02.381933+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/puma-3.12.0/lib/puma/configuration.rb:225:in `call’
    2018-11-09T02:22:02.381934+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/puma-3.12.0/lib/puma/server.rb:658:in `handle_request’
    2018-11-09T02:22:02.381936+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/puma-3.12.0/lib/puma/server.rb:472:in `process_client’
    2018-11-09T02:22:02.381938+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/puma-3.12.0/lib/puma/server.rb:332:in `block in run’
    2018-11-09T02:22:02.381940+00:00 app[web.1]: [6fb4909a-cda5-4c17-ab8e-f62b661af2cd] vendor/bundle/ruby/2.4.0/gems/puma-3.12.0/lib/puma/thread_pool.rb:133:in `block in spawn_thread’
    2018-11-09T02:22:02.382773+00:00 heroku[router]: at=info method=GET path=”/” host=gentle-reaches-75138.herokuapp.com request_id=6fb4909a-cda5-4c17-ab8e-f62b661af2cd fwd=”71.56.81.32″ dyno=web.1 connect=1ms service=20ms status=404 bytes=177 protocol=https

    Reply
    1. Jason Swett Post author

      Hmm, I’m not sure, sorry. What I usually recommend to people is to create a Stack Overflow question and then link it here and/or send me an email at jason@codewithjason.com. That way you’re not dependent on just me to answer and it’s easier for me and others to ask some clarifying questions.

      Reply

Leave a Reply

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