Rails middleware to enable History API for your Single Page Apps

Everybody loves single page apps, but paths containing a pound sign ("#") are a little odd. This is why I like using the History API in my EmberJS powered apps.

Turning on History API in Ember is just the matter of switching an attribute. But on the backend side, your server does not know that incoming paths are meant to be interpreted by your Ember app, not by the server. In larger deploys with a load balancer, you can setup rules to redirect API traffic (e.g. paths containing /api/…) to the backend server, and other traffic to the asset server. But if you're using a smaller setup, like a Heroku deployed app, it's easier to fix right in the Rails app with the Rack::Rewrite gem, as long as you namespace your API. Here's how to do it:

First, add these two gems to your Gemfile and run bundle after that:

gem 'rails_12factor', group: :production # static file serving on Heroku
gem 'rack-rewrite' # for the actual rewriting

Then, add the following section to your config/application.rb:

config.middleware.insert_before(Rack::Runtime, Rack::Rewrite) do
  index_file = Rails.root.join('public', 'index.html').to_s
  send_file(/.*/, index_file, if: ->(rack_env) {
    rack_env['PATH_INFO'] !~ /^\/api.*$/
  })
end

This will direct all /api/… to the backend, all other paths are served by your Ember app in the index.html file. Static files will still be served, though, no matter the path.

This means that your JS app must be contained in the public folder of your Rails app. If you are using a build tool like Ember App Kit, this is easy to achieve:

grunt dist && rsync -av --delete dist/ ../backend/public

Run this from your JS app's folder, with ../backend/public being the path to your Rails app's public folder.

I hope you found this useful. :)

Show Comments