Modo API do Rails em 2026: APIs RESTful, serialização e boas práticas

Dominar o modo API do Rails com boas práticas de design RESTful, serialização JSON com Alba e jsonapi-serializer, estratégias de autenticação e tratamento de erros no Rails 8.

Diagrama do modo API do Rails, APIs RESTful e boas práticas de serialização

O modo API do Rails remove o middleware específico do navegador para entregar um backend enxuto e rápido, projetado especificamente para APIs JSON. Com o Rails 8.1 e o crescente ecossistema de bibliotecas de serialização, construir APIs RESTful de qualidade de produção nunca foi tão simples.

Resumo rápido

O modo API do Rails remove sessões, cookies, views e o middleware do asset pipeline. O resultado: um stack mais leve otimizado para respostas JSON, ideal para backends mobile, microsserviços e SPAs desacopladas.

Criando uma aplicação Rails 8 apenas de API

Criar uma aplicação Rails dedicada a APIs exige uma única flag. A opção --api configura o ApplicationController para herdar de ActionController::API em vez de ActionController::Base, removendo mais de 15 camadas de middleware que não servem a nenhum propósito em um contexto puramente de API.

ruby
# Terminal command
rails new order_service --api --database=postgresql

Esse comando gera um projeto sem templates de view, sem compilação de assets e sem cookies de sessão. O arquivo application.rb resultante inclui config.api_only = true, o que mantém o stack de middleware no mínimo.

Para aplicações Rails full-stack existentes que precisam de um namespace de API, a abordagem é diferente: criar um controller de API base que herde de ActionController::API e montar as rotas de API sob um namespace versionado.

ruby
# app/controllers/api/v1/base_controller.rb
module Api
  module V1
    class BaseController < ActionController::API
      before_action :authenticate_request

      private

      def authenticate_request
        # Token validation logic
      end
    end
  end
end

Esse padrão mantém a aplicação full-stack intacta enquanto adiciona uma camada de API dedicada.

Design de rotas RESTful e estratégias de versionamento

Um design de rotas RESTful limpo impacta diretamente a usabilidade e a manutenibilidade da API. O DSL de routing do Rails torna simples expressar hierarquias de recursos, mas algumas convenções separam as APIs sólidas das frágeis.

ruby
# config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :users, only: [:index, :show, :create, :update] do
        resources :orders, only: [:index, :show, :create]
      end

      resources :products, only: [:index, :show] do
        collection do
          get :search
        end
      end

      resource :session, only: [:create, :destroy]
    end
  end
end

Convenções principais:

  • Versionamento por namespace (/api/v1/) em vez de versionamento por cabeçalho, pela simplicidade e cacheabilidade
  • Aninhamento raso limitado a um único nível: /users/:user_id/orders funciona, mas /users/:user_id/orders/:order_id/items deveria se tornar /orders/:order_id/items
  • Recursos singulares para os endpoints que representam a sessão ou o perfil do usuário atual
Depreciação de versões

Ao aposentar uma versão da API, retornar um HTTP 410 Gone com um corpo JSON apontando para a nova versão, em vez de quebrar silenciosamente os clientes.

Serialização JSON: Alba vs. jsonapi-serializer

A serialização determina como os objetos ActiveRecord se tornam JSON. A escolha do serializador afeta os tempos de resposta, a estrutura do payload e a flexibilidade do contrato da API. Duas bibliotecas dominam o ecossistema Rails em 2026: Alba pela velocidade e simplicidade, e jsonapi-serializer pela conformidade com a especificação JSON:API.

Alba: desempenho sem dependências

O Alba serializa objetos Ruby até 10 vezes mais rápido que alternativas legadas como o ActiveModel::Serializer. Ele não tem dependências, o que o torna ideal para serviços de API leves.

ruby
# app/resources/user_resource.rb
class UserResource
  include Alba::Resource

  root_key :user, :users

  attributes :id, :email, :name, :created_at

  attribute :full_name do |user|
    "#{user.first_name} #{user.last_name}"
  end

  many :orders, resource: OrderResource

  # Conditional attributes based on context
  attribute :admin_notes, if: proc { |user, params|
    params[:current_user]&.admin?
  }
end
ruby
# app/controllers/api/v1/users_controller.rb
class Api::V1::UsersController < Api::V1::BaseController
  def show
    user = User.includes(:orders).find(params[:id])
    render json: UserResource.new(user, params: { current_user: current_user })
  end

  def index
    users = User.where(active: true).page(params[:page])
    render json: UserResource.new(users)
  end
end

jsonapi-serializer: conformidade estrita com a especificação

Quando os consumidores da API esperam respostas no formato JSON:API com as chaves data, type, attributes e relationships, o jsonapi-serializer (o fork mantido do fast_jsonapi da Netflix) cuida da formatação automaticamente.

ruby
# app/serializers/user_serializer.rb
class UserSerializer
  include JSONAPI::Serializer

  set_type :user
  set_id :id

  attributes :email, :name, :created_at

  has_many :orders, serializer: OrderSerializer

  # Cache at the serializer level for high-traffic endpoints
  cache_options store: Rails.cache, namespace: "jsonapi", expires_in: 1.hour
end

A escolha depende do projeto: Alba para APIs internas, microsserviços e backends mobile onde a flexibilidade do payload importa. jsonapi-serializer para APIs públicas onde contratos padronizados reduzem o atrito de integração.

Pronto para mandar bem nas entrevistas de Ruby on Rails?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

Padrões de autenticação para APIs Rails

O Rails 8 introduziu um gerador de autenticação integrado que cria um scaffold de autenticação baseada em sessões. Adaptá-lo ao modo apenas de API exige trocar as sessões por cookies por autenticação baseada em tokens. Dois padrões dominam: JWT para arquiteturas sem estado e tokens bearer opacos para uma revogação mais simples.

JWT com tokens de curta duração

ruby
# app/services/jwt_service.rb
class JwtService
  SECRET = Rails.application.credentials.jwt_secret_key
  ALGORITHM = "HS256"

  def self.encode(payload, exp: 15.minutes.from_now)
    payload[:exp] = exp.to_i
    JWT.encode(payload, SECRET, ALGORITHM)
  end

  def self.decode(token)
    body = JWT.decode(token, SECRET, true, algorithm: ALGORITHM).first
    HashWithIndifferentAccess.new(body)
  rescue JWT::ExpiredSignature, JWT::DecodeError => e
    nil
  end
end
ruby
# app/controllers/concerns/jwt_authenticatable.rb
module JwtAuthenticatable
  extend ActiveSupport::Concern

  included do
    before_action :authenticate_request
  end

  private

  def authenticate_request
    token = request.headers["Authorization"]&.split(" ")&.last
    decoded = JwtService.decode(token)

    if decoded
      @current_user = User.find_by(id: decoded[:user_id])
    end

    render json: { error: "Unauthorized" }, status: :unauthorized unless @current_user
  end

  def current_user
    @current_user
  end
end

Tokens de acesso de curta duração (15 minutos) combinados com a rotação de refresh tokens oferecem um equilíbrio seguro. O token de acesso permanece sem estado, enquanto os refresh tokens armazenados no banco de dados permitem a revogação na troca de senha ou no logout.

Tokens bearer opacos

Para APIs mais simples onde uma consulta ao banco de dados por requisição é aceitável, o has_secure_token oferece uma abordagem direta:

ruby
# app/models/user.rb
class User < ApplicationRecord
  has_secure_password
  has_secure_token :api_token

  def regenerate_api_token!
    regenerate_api_token
  end
end

Os tokens opacos simplificam a revogação (excluir ou regenerar o token), mas exigem uma consulta ao banco de dados em cada requisição autenticada.

Tratamento estruturado de erros em toda a API

Respostas de erro consistentes separam as APIs profissionais dos protótipos. Um tratador de erros centralizado impede que o Rails retorne páginas de erro HTML e garante que cada falha retorne JSON estruturado.

ruby
# app/controllers/concerns/error_handler.rb
module ErrorHandler
  extend ActiveSupport::Concern

  included do
    rescue_from ActiveRecord::RecordNotFound, with: :not_found
    rescue_from ActiveRecord::RecordInvalid, with: :unprocessable_entity
    rescue_from ActionController::ParameterMissing, with: :bad_request
  end

  private

  def not_found(exception)
    render json: {
      error: "not_found",
      message: "Resource not found",
      details: exception.message
    }, status: :not_found
  end

  def unprocessable_entity(exception)
    render json: {
      error: "validation_failed",
      message: "Validation failed",
      details: exception.record.errors.full_messages
    }, status: :unprocessable_entity
  end

  def bad_request(exception)
    render json: {
      error: "bad_request",
      message: "Missing required parameter",
      details: exception.message
    }, status: :bad_request
  end
end

Incluir esse concern no controller de API base. Cada endpoint passa então a retornar erros JSON previsíveis com códigos de status HTTP apropriados, tipos de erro legíveis por máquina e mensagens legíveis por humanos.

Pular o CSRF para autenticação por token

Os controllers de API que usam autenticação baseada em tokens devem pular a verificação CSRF. Adicionar skip_before_action :verify_authenticity_token ou herdar de ActionController::API, que não inclui o middleware CSRF por padrão.

Paginação e otimização de respostas

Consultas sem limite são o caminho mais rápido para a degradação do desempenho. Cada endpoint de listagem deveria paginar os resultados e comunicar claramente os metadados de paginação.

ruby
# app/controllers/api/v1/products_controller.rb
class Api::V1::ProductsController < Api::V1::BaseController
  def index
    products = Product
      .where(active: true)
      .order(created_at: :desc)
      .page(params[:page])
      .per(params[:per_page] || 25)

    render json: {
      data: ProductResource.new(products).serializable_hash,
      meta: {
        current_page: products.current_page,
        total_pages: products.total_pages,
        total_count: products.total_count
      }
    }
  end
end

Além da paginação, três otimizações fazem uma diferença mensurável nos tempos de resposta da API:

  • O eager loading com includes ou preload elimina as consultas N+1 que multiplicam as idas e vindas ao banco de dados
  • Selecionar apenas as colunas necessárias com .select(:id, :name, :price) quando os serializadores usam um subconjunto dos atributos do modelo
  • Os cabeçalhos de cache HTTP via stale? e fresh_when permitem que clientes e CDNs façam cache das respostas sem lógica personalizada

Testando endpoints de API Rails com RSpec

Os testes de API deveriam verificar os códigos de status, a estrutura da resposta e as barreiras de autenticação. Os request specs no RSpec percorrem o stack de middleware completo, o que os torna a representação mais próxima do comportamento real de uma API.

ruby
# spec/requests/api/v1/users_spec.rb
RSpec.describe "Api::V1::Users", type: :request do
  let(:user) { create(:user) }
  let(:token) { JwtService.encode(user_id: user.id) }
  let(:headers) { { "Authorization" => "Bearer #{token}" } }

  describe "GET /api/v1/users/:id" do
    it "returns the user with correct structure" do
      get "/api/v1/users/#{user.id}", headers: headers

      expect(response).to have_http_status(:ok)
      json = JSON.parse(response.body)
      expect(json["user"]).to include(
        "id" => user.id,
        "email" => user.email,
        "name" => user.name
      )
    end

    it "returns 401 without authentication" do
      get "/api/v1/users/#{user.id}"

      expect(response).to have_http_status(:unauthorized)
    end

    it "returns 404 for non-existent user" do
      get "/api/v1/users/0", headers: headers

      expect(response).to have_http_status(:not_found)
      json = JSON.parse(response.body)
      expect(json["error"]).to eq("not_found")
    end
  end
end

Esses testes cobrem os três cenários que cada endpoint de API deveria tratar: a estrutura de uma resposta bem-sucedida, a aplicação da autenticação e o formato das respostas de erro.

Recursos de API do Rails 8.1 que vale a pena adotar

O Rails 8.1 (lançado em outubro de 2025) adiciona capacidades diretamente relevantes para o desenvolvimento de API:

  • Os Continuable Jobs permitem que tarefas em segundo plano de longa duração (importações de dados, processamento em lote) sejam retomadas a partir do último ponto de verificação após deploys ou reinicializações, eliminando o trabalho desperdiçado nos pipelines de jobs em segundo plano
  • O Structured Event Logging via Rails.event.notify(...) emite eventos consumíveis por plataformas APM (Datadog, New Relic) sem código de instrumentação personalizado
  • As Deprecated Associations podem ser marcadas com os modos :warn, :raise ou :notify, ajudando as equipes a eliminar gradualmente os relacionamentos legados em grandes bases de código de API

Esses recursos reduzem o boilerplate nos projetos de API e melhoram a observabilidade desde o início. O guia de migração do Rails 8 cobre o caminho completo de atualização.

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Conclusão

  • Usar o modo --api para serviços de API dedicados para eliminar middleware desnecessário e reduzir a latência de resposta
  • Escolher um serializador que se ajuste ao contrato: Alba para velocidade e flexibilidade, jsonapi-serializer para conformidade estrita com JSON:API
  • Implementar autenticação por token com JWTs de curta duração e rotação de refresh tokens, ou tokens bearer opacos para necessidades de revogação mais simples
  • Centralizar o tratamento de erros em um concern compartilhado para que cada endpoint retorne JSON estruturado com tipos de erro consistentes
  • Paginar cada endpoint de listagem e aplicar eager loading para eliminar as consultas N+1 antes que elas cheguem à produção
  • Testar com request specs os três cenários fundamentais: estrutura de sucesso, aplicação de autenticação e formato de erros
  • Adotar os recursos do Rails 8.1 como os continuable jobs e os eventos estruturados para melhorar a confiabilidade e a observabilidade dos serviços de API

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Tags

#ruby-on-rails
#api
#rest
#serialization
#best-practices

Compartilhar

Artigos relacionados