Modo API de Rails en 2026: API RESTful, serialización y buenas prácticas

Dominar el modo API de Rails con buenas prácticas de diseño RESTful, serialización JSON con Alba y jsonapi-serializer, estrategias de autenticación y manejo de errores en Rails 8.

Diagrama del modo API de Rails, API RESTful y buenas prácticas de serialización

El modo API de Rails elimina el middleware específico del navegador para entregar un backend ligero y rápido, diseñado a propósito para las API JSON. Con Rails 8.1 y el creciente ecosistema de bibliotecas de serialización, construir API RESTful de calidad de producción nunca ha sido tan fluido.

Lo esencial

El modo API de Rails elimina las sesiones, las cookies, las vistas y el middleware del asset pipeline. El resultado: un stack más ligero optimizado para respuestas JSON, ideal para backends móviles, microservicios y SPA desacopladas.

Crear una aplicación Rails 8 solo de API

Crear una aplicación Rails dedicada a API requiere un solo flag. La opción --api configura ApplicationController para que herede de ActionController::API en lugar de ActionController::Base, eliminando más de 15 capas de middleware que no cumplen ningún propósito en un contexto puramente de API.

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

Este comando genera un proyecto sin plantillas de vista, sin compilación de assets ni cookies de sesión. El archivo application.rb resultante incluye config.api_only = true, lo que mantiene el stack de middleware al mínimo.

Para aplicaciones Rails full-stack existentes que necesitan un namespace de API, el enfoque difiere: crear un controlador API base que herede de ActionController::API y montar las rutas de API bajo un 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

Este patrón mantiene intacta la aplicación full-stack mientras añade una capa de API dedicada.

Diseño de rutas RESTful y estrategias de versionado

Un diseño de rutas RESTful limpio impacta directamente en la usabilidad y mantenibilidad de la API. El DSL de routing de Rails facilita expresar jerarquías de recursos, pero unas pocas convenciones separan las API sólidas de las frágiles.

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

Convenciones clave:

  • Versionado por namespace (/api/v1/) en lugar de versionado por cabecera, por simplicidad y cacheabilidad
  • Anidamiento superficial limitado a un solo nivel: /users/:user_id/orders funciona, pero /users/:user_id/orders/:order_id/items debería convertirse en /orders/:order_id/items
  • Recursos singulares para los endpoints que representan la sesión o el perfil del usuario actual
Deprecación de versiones

Al retirar una versión de la API, devolver un HTTP 410 Gone con un cuerpo JSON que apunte a la nueva versión en lugar de romper silenciosamente a los clientes.

Serialización JSON: Alba vs. jsonapi-serializer

La serialización determina cómo los objetos ActiveRecord se convierten en JSON. La elección del serializador afecta los tiempos de respuesta, la estructura del payload y la flexibilidad del contrato de la API. Dos bibliotecas dominan el ecosistema Rails en 2026: Alba por su velocidad y simplicidad, y jsonapi-serializer por su cumplimiento de la especificación JSON:API.

Alba: rendimiento sin dependencias

Alba serializa objetos Ruby hasta 10 veces más rápido que las alternativas heredadas como ActiveModel::Serializer. No tiene dependencias, lo que la hace ideal para servicios de API ligeros.

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: cumplimiento estricto de la especificación

Cuando los consumidores de la API esperan respuestas con formato JSON:API con las claves data, type, attributes y relationships, jsonapi-serializer (el fork mantenido del fast_jsonapi de Netflix) gestiona el formateo automáticamente.

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

La elección depende del proyecto: Alba para API internas, microservicios y backends móviles donde importa la flexibilidad del payload. jsonapi-serializer para API públicas donde los contratos estandarizados reducen la fricción de integración.

¿Listo para aprobar tus entrevistas de Ruby on Rails?

Practica con nuestros simuladores interactivos, flashcards y tests técnicos.

Patrones de autenticación para API Rails

Rails 8 introdujo un generador de autenticación integrado que andamia autenticación basada en sesiones. Adaptarlo al modo solo de API requiere intercambiar las sesiones por cookies por autenticación basada en tokens. Dos patrones dominan: JWT para arquitecturas sin estado y tokens bearer opacos para una revocación más sencilla.

JWT con tokens de corta duración

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

Los tokens de acceso de corta duración (15 minutos) combinados con la rotación de refresh tokens ofrecen un equilibrio seguro. El token de acceso permanece sin estado, mientras que los refresh tokens almacenados en la base de datos permiten la revocación al cambiar la contraseña o cerrar sesión.

Tokens bearer opacos

Para API más simples donde una consulta a la base de datos por petición es aceptable, has_secure_token ofrece un enfoque directo:

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

Los tokens opacos simplifican la revocación (eliminar o regenerar el token) pero requieren una consulta a la base de datos en cada petición autenticada.

Gestión estructurada de errores en toda la API

Las respuestas de error consistentes separan las API profesionales de los prototipos. Un gestor de errores centralizado impide que Rails devuelva páginas de error HTML y garantiza que cada fallo devuelva JSON estructurado.

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 este concern en el controlador API base. Cada endpoint devuelve entonces errores JSON predecibles con códigos de estado HTTP apropiados, tipos de error legibles por máquina y mensajes legibles por humanos.

Omitir CSRF para la autenticación por token

Los controladores de API que usan autenticación basada en tokens deben omitir la verificación CSRF. Añadir skip_before_action :verify_authenticity_token o heredar de ActionController::API, que no incluye el middleware CSRF por defecto.

Paginación y optimización de respuestas

Las consultas sin límite son el camino más rápido hacia la degradación del rendimiento. Cada endpoint de listado debería paginar los resultados y comunicar claramente los metadatos de paginación.

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

Más allá de la paginación, tres optimizaciones marcan una diferencia medible en los tiempos de respuesta de la API:

  • El eager loading con includes o preload elimina las consultas N+1 que multiplican los viajes de ida y vuelta a la base de datos
  • Seleccionar solo las columnas necesarias con .select(:id, :name, :price) cuando los serializadores usan un subconjunto de los atributos del modelo
  • Las cabeceras de caché HTTP mediante stale? y fresh_when permiten a los clientes y CDN cachear respuestas sin lógica personalizada

Probar endpoints de API Rails con RSpec

Las pruebas de API deberían verificar los códigos de estado, la estructura de la respuesta y las barreras de autenticación. Las request specs en RSpec atraviesan el stack de middleware completo, lo que las convierte en la representación más cercana al comportamiento real de una 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

Estas pruebas cubren los tres escenarios que cada endpoint de API debería manejar: la estructura de una respuesta exitosa, la aplicación de la autenticación y el formato de las respuestas de error.

Características de API de Rails 8.1 que vale la pena adoptar

Rails 8.1 (lanzado en octubre de 2025) añade capacidades directamente relevantes para el desarrollo de API:

  • Los Continuable Jobs permiten que las tareas en segundo plano de larga duración (importaciones de datos, procesamiento por lotes) se reanuden desde el último punto de control tras despliegues o reinicios, eliminando el trabajo desperdiciado en los pipelines de jobs en segundo plano
  • El Structured Event Logging mediante Rails.event.notify(...) emite eventos consumibles por plataformas APM (Datadog, New Relic) sin código de instrumentación personalizado
  • Las Deprecated Associations pueden marcarse con los modos :warn, :raise o :notify, ayudando a los equipos a eliminar progresivamente las relaciones heredadas en grandes bases de código de API

Estas características reducen el boilerplate en los proyectos de API y mejoran la observabilidad desde el principio. La guía de migración de Rails 8 cubre la ruta de actualización completa.

¡Empieza a practicar!

Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.

Conclusión

  • Usar el modo --api para servicios de API dedicados para eliminar middleware innecesario y reducir la latencia de respuesta
  • Elegir un serializador que se ajuste al contrato: Alba para velocidad y flexibilidad, jsonapi-serializer para cumplimiento estricto de JSON:API
  • Implementar autenticación por token con JWT de corta duración y rotación de refresh tokens, o tokens bearer opacos para necesidades de revocación más sencillas
  • Centralizar la gestión de errores en un concern compartido para que cada endpoint devuelva JSON estructurado con tipos de error consistentes
  • Paginar cada endpoint de listado y aplicar eager loading para eliminar las consultas N+1 antes de que lleguen a producción
  • Probar con request specs los tres escenarios fundamentales: estructura de éxito, aplicación de autenticación y formato de errores
  • Adoptar las características de Rails 8.1 como los continuable jobs y los eventos estructurados para mejorar la fiabilidad y la observabilidad de los servicios de API

¡Empieza a practicar!

Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.

Etiquetas

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

Compartir

Artículos relacionados