Mode API de Rails en 2026 : API RESTful, sérialisation et bonnes pratiques

Maîtriser le mode API de Rails avec les bonnes pratiques de conception RESTful, la sérialisation JSON avec Alba et jsonapi-serializer, les stratégies d'authentification et la gestion des erreurs dans Rails 8.

Schéma du mode API de Rails, API RESTful et bonnes pratiques de sérialisation

Le mode API de Rails supprime les middlewares spécifiques au navigateur pour livrer un backend léger et rapide, conçu spécifiquement pour les API JSON. Avec Rails 8.1 et l'écosystème grandissant de bibliothèques de sérialisation, construire des API RESTful de qualité production n'a jamais été aussi fluide.

L'essentiel en bref

Le mode API de Rails retire les sessions, les cookies, les vues et le middleware de l'asset pipeline. Le résultat : une stack plus légère optimisée pour les réponses JSON, idéale pour les backends mobiles, les microservices et les SPA découplées.

Créer une application Rails 8 en mode API uniquement

Créer une application Rails dédiée aux API tient en un seul flag. L'option --api configure ApplicationController pour qu'il hérite de ActionController::API au lieu de ActionController::Base, retirant plus de 15 couches de middleware sans aucune utilité dans un contexte purement API.

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

Cette commande génère un projet sans templates de vue, sans compilation d'assets ni cookies de session. Le fichier application.rb résultant inclut config.api_only = true, ce qui maintient la stack de middleware au strict minimum.

Pour les applications Rails full-stack existantes qui nécessitent un namespace API, l'approche diffère : créer un contrôleur API de base qui hérite de ActionController::API et monter les routes API sous un namespace versionné.

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

Ce pattern préserve l'application full-stack intacte tout en ajoutant une couche API dédiée.

Conception des routes RESTful et stratégies de versioning

Une conception de routes RESTful propre influence directement la facilité d'utilisation et la maintenabilité d'une API. Le DSL de routing de Rails rend l'expression des hiérarchies de ressources simple, mais quelques conventions séparent les API solides des API fragiles.

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

Conventions clés :

  • Versioning par namespace (/api/v1/) plutôt que par en-tête, pour la simplicité et la mise en cache
  • Imbrication superficielle limitée à un seul niveau : /users/:user_id/orders fonctionne, mais /users/:user_id/orders/:order_id/items devrait devenir /orders/:order_id/items
  • Ressources singulières pour les endpoints représentant la session ou le profil de l'utilisateur courant
Dépréciation des versions

Lors du retrait d'une version d'API, retourner un HTTP 410 Gone avec un corps JSON pointant vers la nouvelle version plutôt que de casser silencieusement les clients.

Sérialisation JSON : Alba vs. jsonapi-serializer

La sérialisation détermine comment les objets ActiveRecord deviennent du JSON. Le choix du sérialiseur affecte les temps de réponse, la structure de la charge utile et la flexibilité du contrat d'API. Deux bibliothèques dominent l'écosystème Rails en 2026 : Alba pour la vitesse et la simplicité, et jsonapi-serializer pour la conformité à la spécification JSON:API.

Alba : performance sans dépendance

Alba sérialise les objets Ruby jusqu'à 10 fois plus vite que les alternatives héritées comme ActiveModel::Serializer. Elle n'a aucune dépendance, ce qui la rend idéale pour les services API légers.

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 : conformité stricte à la spécification

Lorsque les consommateurs de l'API attendent des réponses au format JSON:API avec les clés data, type, attributes et relationships, jsonapi-serializer (le fork maintenu du fast_jsonapi de Netflix) gère le formatage automatiquement.

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

Le choix dépend du projet : Alba pour les API internes, les microservices et les backends mobiles où la flexibilité de la charge utile compte. jsonapi-serializer pour les API publiques où des contrats standardisés réduisent les frictions d'intégration.

Prêt à réussir tes entretiens Ruby on Rails ?

Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.

Patterns d'authentification pour les API Rails

Rails 8 a introduit un générateur d'authentification intégré qui échafaude une authentification basée sur les sessions. L'adapter au mode API uniquement nécessite de remplacer les sessions par cookies par une authentification basée sur des tokens. Deux patterns dominent : le JWT pour les architectures sans état et les tokens bearer opaques pour une révocation plus simple.

JWT avec tokens à courte durée de vie

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

Des tokens d'accès à courte durée de vie (15 minutes) associés à une rotation des refresh tokens offrent un équilibre sécurisé. Le token d'accès reste sans état, tandis que les refresh tokens stockés en base de données permettent la révocation lors d'un changement de mot de passe ou d'une déconnexion.

Tokens bearer opaques

Pour des API plus simples où une recherche en base de données par requête est acceptable, has_secure_token offre une approche directe :

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

Les tokens opaques simplifient la révocation (supprimer ou régénérer le token) mais exigent une requête en base de données à chaque appel authentifié.

Gestion structurée des erreurs à travers l'API

Des réponses d'erreur cohérentes séparent les API professionnelles des prototypes. Un gestionnaire d'erreurs centralisé empêche Rails de retourner des pages d'erreur HTML et garantit que chaque échec retourne du JSON structuré.

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

Inclure ce concern dans le contrôleur API de base. Chaque endpoint retourne alors des erreurs JSON prévisibles avec des codes de statut HTTP appropriés, des types d'erreur lisibles par machine et des messages lisibles par l'humain.

Ignorer le CSRF pour l'authentification par token

Les contrôleurs API utilisant une authentification basée sur des tokens doivent ignorer la vérification CSRF. Ajouter skip_before_action :verify_authenticity_token ou hériter de ActionController::API, qui n'inclut pas le middleware CSRF par défaut.

Pagination et optimisation des réponses

Les requêtes sans limite sont le chemin le plus rapide vers la dégradation des performances. Chaque endpoint de liste devrait paginer les résultats et communiquer clairement les métadonnées de pagination.

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

Au-delà de la pagination, trois optimisations font une différence mesurable sur les temps de réponse de l'API :

  • L'eager loading avec includes ou preload élimine les requêtes N+1 qui multiplient les allers-retours vers la base de données
  • Sélectionner uniquement les colonnes nécessaires avec .select(:id, :name, :price) quand les sérialiseurs n'utilisent qu'un sous-ensemble des attributs du modèle
  • Les en-têtes de cache HTTP via stale? et fresh_when permettent aux clients et aux CDN de mettre en cache les réponses sans logique personnalisée

Tester les endpoints d'API Rails avec RSpec

Les tests d'API devraient vérifier les codes de statut, la structure des réponses et les barrières d'authentification. Les request specs dans RSpec traversent la stack de middleware complète, ce qui en fait la représentation la plus proche du comportement réel d'une 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

Ces tests couvrent les trois scénarios que chaque endpoint d'API devrait gérer : la structure d'une réponse réussie, l'application de l'authentification et le format des réponses d'erreur.

Fonctionnalités d'API de Rails 8.1 qui valent l'adoption

Rails 8.1 (publié en octobre 2025) ajoute des capacités directement pertinentes pour le développement d'API :

  • Les Continuable Jobs permettent aux tâches d'arrière-plan de longue durée (imports de données, traitement par lots) de reprendre depuis le dernier point de contrôle après des déploiements ou des redémarrages, éliminant le travail gaspillé dans les pipelines de jobs d'arrière-plan
  • Le Structured Event Logging via Rails.event.notify(...) émet des événements consommables par les plateformes APM (Datadog, New Relic) sans code d'instrumentation personnalisé
  • Les Deprecated Associations peuvent être marquées avec les modes :warn, :raise ou :notify, aidant les équipes à retirer progressivement les relations héritées dans de grandes bases de code d'API

Ces fonctionnalités réduisent le boilerplate dans les projets d'API et améliorent l'observabilité dès le départ. Le guide de migration Rails 8 couvre le parcours de mise à niveau complet.

Passe à la pratique !

Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.

Conclusion

  • Utiliser le mode --api pour les services API dédiés afin d'éliminer le middleware superflu et réduire la latence de réponse
  • Choisir un sérialiseur adapté au contrat : Alba pour la vitesse et la flexibilité, jsonapi-serializer pour une conformité stricte à JSON:API
  • Implémenter une authentification par token avec des JWT à courte durée de vie et une rotation des refresh tokens, ou des tokens bearer opaques pour des besoins de révocation plus simples
  • Centraliser la gestion des erreurs dans un concern partagé pour que chaque endpoint retourne du JSON structuré avec des types d'erreur cohérents
  • Paginer chaque endpoint de liste et appliquer l'eager loading pour éliminer les requêtes N+1 avant qu'elles n'atteignent la production
  • Tester avec des request specs sur les trois scénarios fondamentaux : structure de succès, application de l'authentification et format des erreurs
  • Adopter les fonctionnalités de Rails 8.1 comme les continuable jobs et les événements structurés pour améliorer la fiabilité et l'observabilité des services API

Passe à la pratique !

Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.

Tags

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

Partager

Articles similaires