Rails respond_with: how does it work?

Update for Rails 4.2+

#respond_with and ::respond_to (n.b. class method) are no longer a part of Rails. They were migrated into the third-party responders gem as of Rails 4.2 (release notes / commit dated Aug 2014). While responders is not included in Rails by default, it is a dependency of Devise, and thus available in many Rails applications.

The #respond_to instance method, however, is still a part of Rails (5.2rc1 as of this writing).

The official Rails API documentation for ActionController::MimeResponds explains how #respond_to works. The original Rails Guides documentation comments for #respond_with and ::respond_to can still be found in the responders gem source code.


Original Answer

The code for the responders is based in a class and a module. MimeResponds which is included into ActionController::Base, the class your ApplicationController inherits from. Then there is ActionController::Responder which provides the default behavior when using respond_with.


By default, the only behavior rails provides in the response is an implicit attempt to render a template with a name matching the action. Anything beyond that requires more instructions within the action, or a custom respond_to call with a block to handle multiple format responses.

As most controllers use a fairly common pattern of customization, responders provide an extra level of abstraction by introducing more default behavior. Read actions calling to_xml/to_json for specific formats, and mutator actions providing the same as well as redirects for successful mutator actions.


There are a few opportunities to customize how responders behave, from subtle tweaks to completly overriding or extending the behavior.

Class Level: respond_to

Here you specify the formats that the Responder should handle. The formats can be customized as to which actions they will apply to. Each format can be specified with separate calls, allowing complete customization of the actions for each format.

# Responds to html and json on all actions
respond_to :html, :json

# Responds to html and json on index and show actions only.
respond_to :html, :json, :only => [:index,:show]

# Responds to html for everything except show, and json only for index, create and update
respond_to :html, :except => [:show]
respond_to :json, :only => [:index, :create, :update]

Class Level: responder

This is a class attribute that holds the responder. This can be anything that responds to call, which means you can use a proc/lambda or a class that responds to call. Another alternative is to mixin one or modules to the existing responder to overload existing methods, augmenting the default behavior.

class SomeController < ApplicationController
  respond_to :json

  self.responder = proc do |controller, resources, options|
    resource = resources.last
    request = controller.request
    if request.get?
      controller.render json: resource
    elsif request.post? or request.put?
      if resource.errors.any?
        render json: {:status => 'failed', :errors => resource.errors}
      else
        render json: {:status => 'created', :object => resource}
      end
    end
  end
end

While there may be some interesting edge use cases, it's more likely that extending or mixing modules into the default responder would be more common patterns. In any case, the options that are relevant are the resources and options, as they are passed through from the from respond_with.

Instance Level: respond_with

The options here are those that would be passed to render or redirect_to in your controller, but they are only included for success scenarios. For GET actions these would be the render calls, for other actions this would be the options for redirect. Probably the most useful of these is the :location option, which can be used to override that redirect path in case the arguments for respond_with are not sufficient to build the right URL.

# These two are essentially equal
respond_with(:admin, @user, @post)
respond_with(@post, :location => admin_user_post(@user, @post)

# Respond with a 201 instead of a 200 HTTP status code, and also
# redirect to the collection path instead of the resource path
respond_with(@post, :status => :created, :location => posts_path)

# Note that if you want to pass a URL with a query string
# then the location option would be needed.
# /users?scope=active
respond_with(@user, :location => users_path(:scope => 'active'))

As an alternative, the responders gem not only provides some modules for overriding some of the default behavior. It overrides the default responder with an anonymous class that extends the default responder, and provides a class level method for mixing in custom modules to this class. The most useful here is the flash responder, which provides a default set of flashes, delegating customization to the I18n system, config/locales/en.yml by default.

Some examples of custom responders I've used in previous projects include a responder that automatically decorated my resources, and provided a default set of page titles with an interface for easily customizing or overriding the page title.