Veja mais em rubyonrails.org: Mais Ruby on Rails

Using Rails for API-only Applications

In this guide you will learn:

1 O que é uma Aplicação API?

Tradicionalmente, quando as pessoas dizem que usam o Rails como uma "API", elas querem dizer que fornecem uma API acessível junto a suas aplicações web. Por exemplo, o GitHub fornece uma API que você pode usar nas suas próprias aplicações personalizadas.

Com o advento dos frameworks client-side, mais desenvolvedores estão usando o Rails para construir um back-end compartilhado entre suas aplicações web e outros aplicativos nativos.

Por exemplo, o Twitter usa sua API pública em sua aplicação web, que é um site estático que consome recursos via JSON.

Em vez de usar o Rails para gerar HTML que se comunica com o servidor através de formulários e links, muitos desenvolvedores estão tratando suas aplicações web apenas como uma API, separadamente do HTML com JavaScript que apenas consome uma API JSON.

Esse guia aborda a construção de um aplicativo Rails que fornece dados em JSON para um cliente, incluindo frameworks client-side.

2 Why Use Rails for JSON APIs?

The first question a lot of people have when thinking about building a JSON API using Rails is: "isn't using Rails to spit out some JSON overkill? Shouldn't I just use something like Sinatra?".

For very simple APIs, this may be true. However, even in very HTML-heavy applications, most of an application's logic lives outside of the view layer.

The reason most people use Rails is that it provides a set of defaults that allows developers to get up and running quickly, without having to make a lot of trivial decisions.

Let's take a look at some of the things that Rails provides out of the box that are still applicable to API applications.

Handled at the middleware layer:

  • Reloading: Rails applications support transparent reloading. This works even if your application gets big and restarting the server for every request becomes non-viable.
  • Development Mode: Rails applications come with smart defaults for development, making development pleasant without compromising production-time performance.
  • Test Mode: Ditto development mode.
  • Logging: Rails applications log every request, with a level of verbosity appropriate for the current mode. Rails logs in development include information about the request environment, database queries, and basic performance information.
  • Security: Rails detects and thwarts IP spoofing attacks and handles cryptographic signatures in a timing attack aware way. Don't know what an IP spoofing attack or a timing attack is? Exactly.
  • Parameter Parsing: Want to specify your parameters as JSON instead of as a URL-encoded String? No problem. Rails will decode the JSON for you and make it available in params. Want to use nested URL-encoded parameters? That works too.
  • Conditional GETs: Rails handles conditional GET (ETag and Last-Modified) processing request headers and returning the correct response headers and status code. All you need to do is use the stale? check in your controller, and Rails will handle all of the HTTP details for you.
  • HEAD requests: Rails will transparently convert HEAD requests into GET ones, and return just the headers on the way out. This makes HEAD work reliably in all Rails APIs.

While you could obviously build these up in terms of existing Rack middleware, this list demonstrates that the default Rails middleware stack provides a lot of value, even if you're "just generating JSON".

Handled at the Action Pack layer:

  • Resourceful Routing: If you're building a RESTful JSON API, you want to be using the Rails router. Clean and conventional mapping from HTTP to controllers means not having to spend time thinking about how to model your API in terms of HTTP.
  • URL Generation: The flip side of routing is URL generation. A good API based on HTTP includes URLs (see the GitHub Gist API for an example).
  • Header and Redirection Responses: head :no_content and redirect_to user_url(current_user) come in handy. Sure, you could manually add the response headers, but why?
  • Caching: Rails provides page, action, and fragment caching. Fragment caching is especially helpful when building up a nested JSON object.
  • Basic, Digest, and Token Authentication: Rails comes with out-of-the-box support for three kinds of HTTP authentication.
  • Instrumentation: Rails has an instrumentation API that triggers registered handlers for a variety of events, such as action processing, sending a file or data, redirection, and database queries. The payload of each event comes with relevant information (for the action processing event, the payload includes the controller, action, parameters, request format, request method, and the request's full path).
  • Generators: It is often handy to generate a resource and get your model, controller, test stubs, and routes created for you in a single command for further tweaking. Same for migrations and others.
  • Plugins: Many third-party libraries come with support for Rails that reduce or eliminate the cost of setting up and gluing together the library and the web framework. This includes things like overriding default generators, adding Rake tasks, and honoring Rails choices (like the logger and cache back-end).

Of course, the Rails boot process also glues together all registered components. For example, the Rails boot process is what uses your config/database.yml file when configuring Active Record.

The short version is: you may not have thought about which parts of Rails are still applicable even if you remove the view layer, but the answer turns out to be most of it.

3 Configuração básica

Se você estiver construindo uma aplicação Rails que será uma API, você pode começar com um subconjunto mais limitado do Rails e adicionar recursos, conforme necessário.

3.1 Criando uma nova aplicação

Você pode gerar uma nova API Rails:

$ rails new my_api --api

Esse comando fará três coisas principais para você:

  • Configura sua aplicação para começar com um conjunto mais limitado de middlewares que o normal. Especificamente, não serão incluídos middlewares para aplicações web (como suporte a cookies) por padrão.
  • Faz com que o ApplicationController herde do ActionController::API ao invés do ActionController::Base. Como nos middlewares, isso exclui qualquer Action Controller ou Módulo que forneçam funcionalidades usadas primordialmente pelo navegador.
  • Configura os geradores para não gerar views, helpers, e assets quando você criar um novo recurso.

3.2 Alterando uma aplicação existente

Se você deseja usar uma aplicação que já existe e transformá-la em API, siga os passos a seguir.

Em config/application.rb adicione a seguinte linha no começo da classe Application:

config.api_only = true

Em config/environments/development.rb, defina config.debug_exception_response_format para configurar o formato usado nas respostas quando ocorrer um erro no modo de desenvolvimento.

Para renderizar uma página HTML com as informações de debugging, use o valor :default.

config.debug_exception_response_format = :default

Para renderizar as informações de debugging preservando o formato da resposta, use o valor :api.

config.debug_exception_response_format = :api

Por padrão, config.debug_exception_response_format está definido para :api, quando config.api_only está com o valor true.

Finalmente, no arquivo app/controllers/application_controller.rb, ao invés de

class ApplicationController < ActionController::Base
end

troque por:

class ApplicationController < ActionController::API
end

4 Choosing Middleware

An API application comes with the following middleware by default:

  • Rack::Sendfile
  • ActionDispatch::Static
  • ActionDispatch::Executor
  • ActiveSupport::Cache::Strategy::LocalCache::Middleware
  • Rack::Runtime
  • ActionDispatch::RequestId
  • ActionDispatch::RemoteIp
  • Rails::Rack::Logger
  • ActionDispatch::ShowExceptions
  • ActionDispatch::DebugExceptions
  • ActionDispatch::Reloader
  • ActionDispatch::Callbacks
  • ActiveRecord::Migration::CheckPending
  • Rack::Head
  • Rack::ConditionalGet
  • Rack::ETag

See the internal middleware section of the Rack guide for further information on them.

Other plugins, including Active Record, may add additional middleware. In general, these middleware are agnostic to the type of application you are building, and make sense in an API-only Rails application.

You can get a list of all middleware in your application via:

$ rails middleware

4.1 Using the Cache Middleware

By default, Rails will add a middleware that provides a cache store based on the configuration of your application (memcache by default). This means that the built-in HTTP cache will rely on it.

For instance, using the stale? method:

def show
  @post = Post.find(params[:id])

  if stale?(last_modified: @post.updated_at)
    render json: @post
  end
end

The call to stale? will compare the If-Modified-Since header in the request with @post.updated_at. If the header is newer than the last modified, this action will return a "304 Not Modified" response. Otherwise, it will render the response and include a Last-Modified header in it.

Normally, this mechanism is used on a per-client basis. The cache middleware allows us to share this caching mechanism across clients. We can enable cross-client caching in the call to stale?:

def show
  @post = Post.find(params[:id])

  if stale?(last_modified: @post.updated_at, public: true)
    render json: @post
  end
end

This means that the cache middleware will store off the Last-Modified value for a URL in the Rails cache, and add an If-Modified-Since header to any subsequent inbound requests for the same URL.

Think of it as page caching using HTTP semantics.

4.2 Using Rack::Sendfile

When you use the send_file method inside a Rails controller, it sets the X-Sendfile header. Rack::Sendfile is responsible for actually sending the file.

If your front-end server supports accelerated file sending, Rack::Sendfile will offload the actual file sending work to the front-end server.

You can configure the name of the header that your front-end server uses for this purpose using config.action_dispatch.x_sendfile_header in the appropriate environment's configuration file.

You can learn more about how to use Rack::Sendfile with popular front-ends in the Rack::Sendfile documentation.

Here are some values for this header for some popular servers, once these servers are configured to support accelerated file sending:

# Apache and lighttpd
config.action_dispatch.x_sendfile_header = "X-Sendfile"

# Nginx
config.action_dispatch.x_sendfile_header = "X-Accel-Redirect"

Make sure to configure your server to support these options following the instructions in the Rack::Sendfile documentation.

4.3 Using ActionDispatch::Request

ActionDispatch::Request#params will take parameters from the client in the JSON format and make them available in your controller inside params.

To use this, your client will need to make a request with JSON-encoded parameters and specify the Content-Type as application/json.

Here's an example in jQuery:

jQuery.ajax({
  type: 'POST',
  url: '/people',
  dataType: 'json',
  contentType: 'application/json',
  data: JSON.stringify({ person: { firstName: "Yehuda", lastName: "Katz" } }),
  success: function(json) { }
});

ActionDispatch::Request will see the Content-Type and your parameters will be:

{ :person => { :firstName => "Yehuda", :lastName => "Katz" } }

4.4 Other Middleware

Rails ships with a number of other middleware that you might want to use in an API application, especially if one of your API clients is the browser:

  • Rack::MethodOverride
  • ActionDispatch::Cookies
  • ActionDispatch::Flash
  • For session management
    • ActionDispatch::Session::CacheStore
    • ActionDispatch::Session::CookieStore
    • ActionDispatch::Session::MemCacheStore

Any of these middleware can be added via:

config.middleware.use Rack::MethodOverride

4.5 Removing Middleware

If you don't want to use a middleware that is included by default in the API-only middleware set, you can remove it with:

config.middleware.delete ::Rack::Sendfile

Keep in mind that removing these middlewares will remove support for certain features in Action Controller.

5 Escolhendo os módulos do controller

Uma aplicação API (utilizando ActionController::API) vem com os seguintes módulos do controller por padrão:

  • ActionController::UrlFor: Faz com que url_for e helpers similares sejam disponíveis.
  • ActionController::Redirecting: Suporte para redirect_to.
  • AbstractController::Rendering e ActionController::ApiRendering: Suporte básico para renderização.
  • ActionController::Renderers::All: Suporte para render :json e similares.
  • ActionController::ConditionalGet: Suporte para stale?.
  • ActionController::BasicImplicitRender: Certifica-se de retornar uma resposta vazia, se não houver uma explícita.
  • ActionController::StrongParameters: Suporte para filtragem de parâmetros em conjunto com a atribuição do ActiveModel.
  • ActionController::DataStreaming: Suporte para send_file e send_data.
  • AbstractController::Callbacks: Suporte para before_action e helpers similares.
  • ActionController::Rescue: Suporte para rescue_from.
  • ActionController::Instrumentation: Suporte para ganchos de instrumentação definidos pela Action Controller (veja o guia da instrumentação para mais informações a respeito disso)
  • ActionController::ParamsWrapper: Agrupa o hash dos parâmetros em um hash encadeado, para que você não precise especificar elementos raiz enviando requisições POST, por exemplo.
  • ActionController::Head: Suporte para o retorno de uma resposta sem conteúdo, apenas headers

Outros plugins podem adicionar mais módulos. Você pode obter uma lista de todos os módulos incluídos no ActionController::API no console do Rails:

$ rails c
>> ActionController::API.ancestors - ActionController::Metal.ancestors
=> [ActionController::API,
    ActiveRecord::Railties::ControllerRuntime,
    ActionDispatch::Routing::RouteSet::MountedHelpers,
    ActionController::ParamsWrapper,
    ... ,
    AbstractController::Rendering,
    ActionView::ViewPaths]

5.1 Adicionando Outros Módulos

Todos os módulos do Action Controller conhecem seus módulos dependentes. Assim, você pode incluir qualquer módulo em seus controllers, e todas as dependências serão incluídas e configuradas também.

Alguns módulos comuns que você pode querer adicionar:

  • AbstractController::Translation: Suporte para os métodos de localização e tradução l e t
  • Suporte para autenticações HTTP basic, digest ou por token:
    • ActionController::HttpAuthentication::Basic::ControllerMethods,
    • ActionController::HttpAuthentication::Digest::ControllerMethods,
    • ActionController::HttpAuthentication::Token::ControllerMethods
  • ActionView::Layouts: Suporte para layouts ao renderizar.
  • ActionController::MimeResponds: Suporte para respond_to.
  • ActionController::Cookies: Suporte para cookies, que inclui suporte para cookies assinados e criptografados. Isso requer um middleware de cookies
  • ActionController::Caching: Suporte para cache da view do controller da API. Lembre-se que você precisará especificar manualmente o armazenamento em cache dentro do controller, como por exemplo: ruby class ApplicationController < ActionController::API include ::ActionController::Caching self.cache_store = :mem_cache_store end O Rails não faz essa configuração automaticamente

O melhor lugar para adicionar um módulo é em sua ApplicationController, mas você também pode adicionar módulos em controllers individuais.

Feedback

Você é incentivado a ajudar a melhorar a qualidade deste guia.

Por favor, contribua se vir qualquer erros, inclusive erros de digitação. Para começar, você pode ler nossa sessão de contribuindo com a documentação.

Você também pode encontrar conteúdo incompleto ou coisas que não estão atualizadas. Por favor, adicione qualquer documentação em falta na master do Rails. Certifique-se de checar o Edge Guides (en-US) primeiro para verificar se o problema já foi resolvido ou não no branch master. Verifique as Diretrizes do Guia Ruby on Rails para estilo e convenções.

Se, por qualquer motivo, você encontrar algo para consertar, mas não conseguir consertá-lo, por favor abra uma issue no nosso Guia.

E por último, mas não menos importante, qualquer tipo de discussão sobre a documentação do Ruby on Rails é muito bem vinda na lista de discussão rubyonrails-docs e nas issues do Guia em português.