Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker

Building a fast, lightweight REST service with Rails 3

Pivotal Labs
Friday, September 17, 2010

I recently started building a Rails 3 app that will function as an internal REST service. I wanted it to be as lightweight and fast as possible, both to test and to run. Here are a few ways I configured the app to be a bit faster:

  • Defined a limited route set
  • Removed ActiveResource
  • Removed unnecessary middleware
  • Created a custom controller that inherits from Metal

First, I defined the public api for my app in the routes file. I chose not to use resources because

  • I don’t need named routes
  • I wanted the :id parameter for the show action to be named :account_id instead of id (a pet peeve of mine with Rails routing)
  • I only needed 3 of the routes generated by resources

The routes file ended up looking like this:

scope "/api/v1" do
  scope "/accounts" do
    post "/" => "accounts#create"
    get ":account_id" => "accounts#show"
    get ":account_id/transactions" => "accounts#transactions"
    put ":account_id" => "accounts#update"
  end
end

Next, to get rid of ActiveResource, in application.rb I deleted the line the requires rails/all and replaced it with:

# config/appliction.rb
require "rails"

%w(
  active_record
  action_controller
  action_mailer
).each do |framework|
  begin
    require "#{framework}/railtie"
  rescue LoadError
  end
end

Rails has a lot of middleware that I didn’t need for a stateless service like this. This app will be run as a single-threaded app, behind a firewall, with no session or cookies, no views, it will only be accessed via an HTTP library and I’ll likely never open it in a browser even locally. As such, I added this to my application file:

# config/application.rb
[
  Rack::Sendfile,
  ActionDispatch::Flash,
  ActionDispatch::Session::CookieStore,
  ActionDispatch::Cookies,
  ActionDispatch::BestStandardsSupport,
  Rack::MethodOverride,
  ActionDispatch::ShowExceptions,
  ActionDispatch::Static,
  ActionDispatch::RemoteIp,
  ActionDispatch::ParamsParser,
  Rack::Lock,
  ActionDispatch::Head
].each do |klass|
  config.middleware.delete klass
end

# config/environments/production.rb
config.middleware.delete ActiveRecord::ConnectionAdapters::ConnectionManagement

I was surprised by that last one. It’s a rack middleware class sitting in ActiveRecord whose sole purpose is to not close connections in test mode. It seems to me that Rails should just include that class in environments/test.rb, as opposed to having it in every environment. It probably doesn’t matter much, since it’s only going to add a few nanoseconds to each request.

I didn’t need any view rendering at all, so I deleted app/views and app/helpers, then created my own ApplicationController class with just the modules I needed:

class ApplicationController < ActionController::Metal
  include AbstractController::Logger
  include Rails.application.routes.url_helpers
  include ActionController::UrlFor
  include ActionController::Rendering
  include ActionController::Renderers::All
  include ActionController::MimeResponds
end

In my case this was the minimum set of modules necessary to make my tests pass. Unfortunatley, these modules are still very poorly documented, so to figure out which ones I needed I had to:

  • Copy all the code from ActionController::Base into my ApplicationController
  • Delete modules one by one, testing after each one was removed and only leaving in the ones that broke the tests when they were removed

In my controller I just used render :json => object, as opposed to the new respond_to because I don’t really care to check the accept header for every request. For now there will only be json responses, regardless of what you ask for, so I didn’t bother with adding the additional overhead. Here’s what an action looks like:

class AccountsController < ApplicationController
  def show
    render :json => Account.find(params[:account_id])
  end
end

I normally use cucumber for integration testing, but for an app like this I decided to use rspec 2′s new request spec format, which is lighter but still exercises the stack. It looks something like this:

# spec/requests/api_spec.rb
require 'spec_helper'
describe "api" do
  describe "GET /api/v1/accounts/:account_id" do
    it "returns a json hash with the proper data" do
      get "/api/v1/accounts/abc123"
      Yajl::Parser.parse(response.body).should == { "id" => "abc123", "billing_date" => "12/12/2009" }
    end
  end
end

In the end, this has translated to slightly faster load times, faster test runs and faster responses in production without altering the things I like best about Rails.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Add New Comment Cancel reply

Your email address will not be published.

Pivotal Labs

Pivotal Labs

Recent Posts

  • Does the set of all sets contain itself?
  • Standup 3/8/2012
  • Standup 3/7/2012
Subscribe to Pivotal's Feed

Author Topics

riddles (1)
agile (167)
capistrano (2)
rails (26)
movember (1)
git (10)
railsdoc (1)
object-design (1)
bdd (3)
cucumber (3)
linkedin (1)
oauth (1)
ruby (17)
tdd (2)
lvh.me (1)
rails 3.1.1 (1)
selenium (6)
homebrew (1)
mysql (5)
rvm (1)
sproutcore (1)
paperclip (2)
pry (1)
amazon (1)
heroku (1)
rails3 (2)
jasmine (3)
design (3)
process (12)
productivity (8)
learning (1)
olin (1)
migrations (2)
mongodb (2)
devise (2)
javascript (13)
rubymine (4)
ipad (1)
whurl (1)
head.js (1)
pairing (2)
tools (4)
pair programming (1)
rspec (10)
rspec2 (1)
ruby19 (1)
incubation (3)
startup (5)
api (1)
presenter (1)
vanna (1)
pivotal tracker (5)
capybara (1)
fakeweb (1)
webmock (1)
intern (1)
ruby on rails (25)
meetup (1)
textmate (1)
testing (20)
solr (4)
nyc-standup (11)
community (1)
opensource (3)
activerecord (4)
chrome (1)
mp4 (1)
activeresource (1)
flash (3)
neo4j (1)
nginx (1)
rsoc (1)
meta programming (1)
agile standup (7)
government (3)
webos (4)
xss (1)
jquery (1)
bundler (2)
ci (3)
gems (5)
postgresql (1)
geminstaller (1)
gemcutter (1)
cloud (2)
rack (2)
refraction (1)
gem (5)
refactoring (1)
validations (1)
webrat (1)
engine-yard (1)
firefox (2)
jsunit (1)
mongrel (2)
thin (1)
unicorn (1)
facebook (1)
rubygems (5)
jruby (1)
actioncontroller (1)
rails 2.3 (1)
palmpre (1)
autotest (1)
mac (2)
hosting (1)
goruco (11)
database (3)
railsconf (11)
gogaruco (4)
deployment (4)
github (1)
ie (1)
ajax (1)
intellij (1)
json (1)
asset packaging (1)
polonium (1)
character encoding (1)
utf-8 (1)
test (3)
civics (1)
hpricot (1)
rake (3)
sms (1)
unicode (1)
iphone (1)
java (1)
safari (1)
memory leaks (1)
rr (3)
editor (1)
css (1)
nyc (3)
performance (5)
fun (5)
enterprise rails (1)
health (1)
new and cool (1)
general (2)
treetop (1)
errors (1)
stack (1)
trace (1)
cache (1)
cookies (1)
freesoftware (1)
conferences (1)
development (1)
driven (1)
proxy (1)
caching (1)
peertopatent (1)
languages (1)
rest (2)
rubyforge (1)
sake (1)
file (1)
upload (1)
constants (1)
osx (1)
terminal (1)
pairprogramming (2)
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >