Backend once and Client everywhere with Rails-API

April 10, 2014

Normally when you build a web or mobile application, you will have to build seperate backend because most web framework originally means to generate data along with dynamic HTML templates. But if we think that web application is just a form of native applications and we treat it that way, generate only data and just use static HTML to handle the logic. This way, we can reuse our backend to almost every native applications we want.

If you’re asking for some BIG example out there that use this trick, Try

Why use Rails ?

  • Huge resources and communities that provide almost everything you want to build applications.
  • Very clean Routing and easy to use.
  • Secure enough.
  • Easiest (in my opinion) to debug using better_errors and binding_of_caller.
  • I want to use Rails, that’s all.

Beyond this point require Ruby and Rails installed already.

Getting started: Rails-API

Assuming that you’ve already installed Ruby(1.9.3+) and Rails(3.2+) already, today we use this nicely done gem to help us prepare new application

$ gem install rails-api
$ rails-api new contact-api

with Rails-API we’ll get only essential part of Rails to build API instead of getting all heavy and unnecessary features from traditional Rails. And now we have our newly created app called contact-api

Then put this on your gemfile and run “bundle install”, this will make your life a lot easier.

group :development do
gem 'better_errors'
gem 'binding_of_caller'

Rails time

Since there’s only API, so we don’t need to setup things like HTML template or homepage here. Let’s generate contact by using

$ rails g scaffold contact name:string email:string
$ rake db:migrate

If you’re running on Rails 3+, you need to put these line in your app/model/contact.rb

attr_accessible :name, :email

If Rails 4+, in app/controllers/contacts_controller

def create
@contact =
render json: @contact, status: :created, location: @contact
render json: @contact.errors, status: :unprocessable_entity
def contact_params
params.require(:contact).permit(:name, :email)

and then run your rails server with

$ rails s

Try access by your browser to localhost:3000/contacts and you will see an empty JSON “[]” there. Now let’s try to make it error by access to localhost:3000/contacts/test and you’ll see error like below.

Obviously, it’s a “Not found” error because we try to access object Contact that have id = “test” which doesn’t exist. On the right side, you’ll see a very handy shell that you can play with it like how you play in “rails console”. And since we have our console right before our eyes, we should try something.

Better-error shell
Contact.create!(name:"John", email:"[email protected]")

and result is

Now we can try to access localhost:3000/contacts/1 and you will see JSON of your newly created contact. title: 'image'

Prepare for CORS

What we trying to do here is to actually access to Rails server(localhost:3000) from another server instance. So it’ll inevitably face with CORS problem and here we’ll cover that problem before it’s occured with rack-cors

Put this line in Gemfile

gem 'rack-cors', :require => 'rack/cors'


bundle install

Finally, put this code in config/application.rb inside class Application and it’ll look like this.

module ContactApi
class Application < Rails::Application
config.middleware.use Rack::Cors do
allow do
origins '*'
resource '*', :headers => :any, :methods => [:get, :post, :options]

Restart Rails server and it’ll become accessible from every origin.

HTML time

It’s time to build our static site to serve as a native client for contact-api. But before that, we have to choose which server to serve our static files. It can be many things such as another Rails, Node.js, PHP, etc. Whichever is fine but I choose to go with Go, no reason, I just want it. (See details of how easy Go handle static file in previous post)

Below is my jsFiddle demo using angularjs as a client to add contact object by using Rails backend as API only. Or you can play a demo at this Google App Engine Site

All the code can be found at Github