Controller tricks: API on Metal

ActionController::Base

class Api::V1::BaseController < ActionController::Metal include ActionController::Rendering # enables rendering include ActionController::MimeResponds # enables serving different content types like :xml or :json include AbstractController::Callbacks # callbacks for your authentication logic append_view_path "#{Rails.root}/app/views" # you have to specify your views location as well end

Routing: use versioning

BaseController

V1

namespace :api do namespace :v1 do # put your routes here end end

Views tricks: RABL ’em all

object @object attribute :public_id => :id attributes :title, :created_at, :source child :contacts do attributes :title end child :files do attributes :filename, :content_type, :size end

respond_to

def show @object = Object.find(params[:id]) respond_to do |f| f.json { render json: @object.to_json } f.xml { render xml: @object.to_xml } end end

def show @object = Object.find(params[:id]) end

Security

SecureRandom.urlsafe_base64

def build_token begin token = SecureRandom.urlsafe_base64 end while User.exists?(api_token: token) token end

Hiding your IDs with GUIDs

a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11

SecureRandom.uuid

uuid

:string

uuid

execute "ALTER TABLE objects add COLUMN public_id uuid NOT NULL DEFAULT uuid_generate_v4()" add_index :objects, [:public_id], name: "index_objects_on_public_id", unique: true

uuid_generate_v4

create extension "uuid-ossp"

:sql

config/application.rb

config.active_record.schema_format = :sql

Testing your API

Rack::Test::Methods

# spec_helper module ApiHelper include Rack::Test::Methods def app Rails.application end end RSpec.configure do |config| config.include ApiHelper, api: true end # spec require 'spec_helper' describe "api/v1/objects", api: true do let(:url) { "api/v1/objects" } let(:object) { Factory.create(:object) } let(:token) { "YOUR_SECRET_TOKEN" } let(:data) { JSON.parse(last_response.body) } before(:each) { get url, token: token } # here's where API call made subject { data } it { should have(1).object } end

Conclusion

Minimum usable Metal controller

Simple versioning

Easy API views with RABL

Postresql native uuid type

type API spec sample

render json: your_json

include ActionController::Renderers::All

This article is about gotchas you should be aware of while building your API with Ruby on Rails.Sooner or later each Rails developer come to a point when he wants to build his first API. Among the first things you have to take care of are your controllers. If you want your API to be fast (and I bet you do) then you should consider using ActionController::Metal . The trick is thathas many Middlewares that are not necessary for the API, by using Metal controller with minimum modules included, the one can achieve up to 40% speedup . Lets see what your basic metal controller may looks like:Unfortunately NewRelic doesn’t support Metal by default, so you have to add monitoring manually Nobody’s perfect. So are we. Your API will definitely be changed and extended multiple times in the future so you better take care of your versioning at the beginning. As you noticed,wrapped innamespace. Use something like this in your routes:You don’t want to burden your code with logic of exposing different model fields for different API actions, right? In this case you should use some template engine. RABL is at your service. Here’s an example of your view:Also that will save you time by getting rid of uglyblocks. Instead ofYou can simple do literally NothingJust make sure you have a RABL view in a corresponding directory.There’re plenty of articles about securing your API with OAuth. Another convenient way is to simply use token passed in the query string. If you ended up with token keep in mind that you can easily generate it by calling. To make sure token is unique you can use something like this:By default Rails uses incremental integer for primary key. Common practice is not to expose these kind of IDs to the public via your API because users can guess other IDs in your database and that might be a potential risk. To solve this you can come up with a simple algorithm that will convert your IDs into some “safe” form and back. But still it’s not super safe because someone can find out what the algorithm is. Another possible solution is to expose GUIDs to the public. It’s a 128 entity that typically looks like this:To generate it in Ruby use(generates V4 GUID). You can store it as a simple string column but if you are using Postgresql then you can utilize it’s built intype – this can save you a lot of space . Rails, however falls back tofor thePostgresql native type. To workaround this you can create column manually in your migration (index should be added as well):To usefunction you have to add Postgresql extension:And don’t forget to change your schema format toinYou definitely want to cover up your new shiny API with some sort of tests. See example below that usesWe’ve just gone through the following tricks:Sure, you’ll face a lot more issues in the wild but these basics intended to help you start up quickly. The last, but not the least – don’t forget about good documentation. Your API users will definitely appreciate it.If you want to try all these tricks in the wild see recently released API for our dummy SMTP server – Mailtrap.io If you want to useyou shouldin your base API controller. Thanksfor pointing that out.