Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails API design

I have a rails site which is mainly REST based and I want to add JSON API support.

For a clean code base, should I add this support within the existing controllers or create new controllers which only handle this API methods and then move all the common code to models/helpers?

like image 984
ESoft Avatar asked Dec 11 '12 07:12

ESoft


2 Answers

I used both techniques: writing API logic in the same controllers and making separate controllers for the API request.

If it's only an API, a small app used only by you, go with the default controller-model relation that rails offers. The code will be quite clean. Your routes file will be also clean.

If you have a website and you want to build an API along, do it separately. I've build one alongside the existing controllers and the code was too messy. I refactored the code several times, but I still didn't like it (it's also a matter of personal taste).

Another solution is to make controllers with a prefix. Ex: ApiUsersController. This would make your routes.rb file look ugly because you would have to manually specify the route to match the corresponding controller method.

The working solution for me was to move all the API logic in separate controllers under an API namespace. Namespaces also let you do API versioning. So, for example, your routes will be:

GET /api/v1/users.json
POST /api/v1/users.json

And then, in the future you can create another API version, let's say v2, without breaking existing applications that use the older version of the API.

You can find more about namespaces here: http://guides.rubyonrails.org/routing.html#controller-namespaces-and-routing

An awesome tutorial about REST-full API with versioning: http://railscasts.com/episodes/350-rest-api-versioning?view=asciicast

like image 64
Ilea Cristian Avatar answered Oct 21 '22 22:10

Ilea Cristian


Rails controller generators implement JSON responses by default.

For example, if you have this method:

class UsersController < ApplicationController
  def index
    @users = User.all
  end
end

You could add JSON response like so

class UsersController < Application Controller
  def index
    respond_to do |format|
      format.html
      format.js { render :json => @users }
    end
  end
end

Now, you have two responses for /users

  1. http://someapp.com/users
  2. http://someapp.com/users.json

You can add another one very easily; e.g.,

format.xml { render :xml => @users }

Now your app will respond to http://someapp.com/users.xml


Customizing your json

Chances are you won't want to output all the fields of a table in your json. For that, look to rails/jbuilder. It lets you create JSON structures with a builder-style DSL.

An example from the jbuilder README

Jbuilder.encode do |json|
  json.content format_content(@message.content)
  json.(@message, :created_at, :updated_at)

  json.author do
    json.name @message.creator.name.familiar
    json.email_address @message.creator.email_address_with_name
    json.url url_for(@message.creator, format: :json)
  end

  if current_user.admin?
    json.visitors calculate_visitors(@message)
  end

  json.comments @message.comments, :content, :created_at

  json.attachments @message.attachments do |attachment|
    json.filename attachment.filename
    json.url url_for(attachment)
  end
end

Produces the following output:

{ 
  "content": "<p>This is <i>serious</i> monkey business",
  "created_at": "2011-10-29T20:45:28-05:00",
  "updated_at": "2011-10-29T20:45:28-05:00",

  "author": {
    "name": "David H.",
    "email_address": "'David Heinemeier Hansson' <[email protected]>",
    "url": "http://example.com/users/1-david.json"
  },

  "visitors": 15,

  "comments": [
    { "content": "Hello everyone!", "created_at": "2011-10-29T20:45:28-05:00" },
    { "content": "To you my good sir!", "created_at": "2011-10-29T20:47:28-05:00" }
  ],

  "attachments": [
    { "filename": "forecast.xls", "url": "http://example.com/downloads/forecast.xls" },
    { "filename": "presentation.pdf", "url": "http://example.com/downloads/presentation.pdf" }
  ]
}
like image 22
maček Avatar answered Oct 22 '22 00:10

maček