API GraphQL avec Rails en 2026 : graphql-ruby, Subscriptions et Questions d'Entretien
Construire une API GraphQL prête pour la production avec Rails 8 et graphql-ruby. Conception de schéma, mutations, subscriptions avec ActionCable et préparation aux entretiens.

GraphQL est devenu le choix privilégié pour les APIs nécessitant une récupération de données flexible, et graphql-ruby apporte cette fonctionnalité à Rails avec une intégration étroite à ActiveRecord. Rails 8, sorti fin 2024, fonctionne parfaitement avec graphql-ruby 2.4, qui a introduit une gestion améliorée des subscriptions et une meilleure exécution paresseuse pour la prévention des problèmes N+1.
La gem graphql-ruby fournit une implémentation complète de GraphQL : définition de schéma avec un DSL Ruby, inférence automatique des types depuis les modèles ActiveRecord, DataLoader intégré pour le batching, et intégration ActionCable pour les subscriptions en temps réel.
Installation de graphql-ruby dans une Application Rails 8
Le générateur de la gem crée la structure initiale du schéma et monte le endpoint GraphQL. Il suffit de commencer avec la gem et d'exécuter le générateur d'installation pour créer les fichiers de base.
# Gemfile
gem 'graphql', '~> 2.4'# Terminal
bundle install
rails generate graphql:installLe générateur crée le répertoire app/graphql/ avec le fichier de schéma, les types de base et un répertoire mutations. Il ajoute également une route vers /graphql et monte optionnellement GraphiQL pour le développement.
# app/graphql/sharpskill_schema.rb
class SharpskillSchema < GraphQL::Schema
mutation(Types::MutationType)
query(Types::QueryType)
subscription(Types::SubscriptionType)
# Utiliser le DataLoader intégré pour prévenir les N+1
use GraphQL::Dataloader
# Requis pour les subscriptions
use GraphQL::Subscriptions::ActionCableSubscriptions
endLe fichier de schéma sert de point d'entrée. Il déclare quels types gèrent les queries, mutations et subscriptions, et enregistre tout middleware comme DataLoader.
Définition des Types et Resolvers pour les Modèles Rails
Chaque modèle ActiveRecord qui apparaît dans l'API nécessite un type GraphQL correspondant. Le type définit quels champs sont exposés et comment ils sont résolus.
# app/graphql/types/user_type.rb
module Types
class UserType < Types::BaseObject
field :id, ID, null: false
field :email, String, null: false
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
# Association avec batching automatique via DataLoader
field :posts, [Types::PostType], null: false
def posts
dataloader.with(Sources::ActiveRecordCollection, Post, :user_id).load(object.id)
end
end
endL'appel dataloader.with regroupe plusieurs récupérations de posts en une seule requête SQL. Sans DataLoader, une query demandant 50 utilisateurs avec leurs posts exécuterait 51 requêtes. Avec DataLoader, elle n'en exécute que 2.
# app/graphql/sources/active_record_collection.rb
class Sources::ActiveRecordCollection < GraphQL::Dataloader::Source
def initialize(model, foreign_key)
@model = model
@foreign_key = foreign_key
end
def fetch(ids)
records = @model.where(@foreign_key => ids).group_by(&@foreign_key)
ids.map { |id| records[id] || [] }
end
endCe pattern apparaît dans tout projet graphql-ruby sérieux. La documentation officielle de DataLoader couvre des cas d'utilisation supplémentaires comme le caching et les sources imbriquées.
Construction de Queries avec Arguments et Filtrage
Le QueryType définit les champs racine disponibles pour les clients. Les arguments permettent le filtrage et la pagination.
# app/graphql/types/query_type.rb
module Types
class QueryType < Types::BaseObject
field :users, [Types::UserType], null: false do
argument :email_contains, String, required: false
argument :limit, Integer, required: false, default_value: 20
end
def users(email_contains: nil, limit:)
scope = User.all
scope = scope.where('email ILIKE ?', "%#{email_contains}%") if email_contains
scope.limit(limit)
end
field :user, Types::UserType, null: true do
argument :id, ID, required: true
end
def user(id:)
User.find_by(id: id)
end
end
endLes méthodes resolver reçoivent des arguments nommés correspondant à la définition du champ. Retourner nil depuis un champ nullable est valide ; lever une erreur depuis un champ non-null déclenche une réponse d'erreur GraphQL.
Prêt à réussir tes entretiens Ruby on Rails ?
Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.
Mutations : Création et Mise à Jour d'Enregistrements
Les mutations suivent une convention plus stricte que les queries. Chaque mutation vit dans sa propre classe et retourne un type payload qui inclut à la fois le résultat et les erreurs éventuelles.
# app/graphql/mutations/create_post.rb
module Mutations
class CreatePost < Mutations::BaseMutation
argument :title, String, required: true
argument :body, String, required: true
field :post, Types::PostType, null: true
field :errors, [String], null: false
def resolve(title:, body:)
post = context[:current_user].posts.build(title: title, body: body)
if post.save
{ post: post, errors: [] }
else
{ post: nil, errors: post.errors.full_messages }
end
end
end
endLe context[:current_user] provient du contrôleur. L'authentification se fait avant la couche GraphQL, généralement en utilisant Devise ou une bibliothèque JWT.
# app/controllers/graphql_controller.rb
class GraphqlController < ApplicationController
def execute
context = {
current_user: current_user,
request: request
}
result = SharpskillSchema.execute(
params[:query],
variables: params[:variables],
context: context,
operation_name: params[:operationName]
)
render json: result
end
endLe contrôle d'accès à l'intérieur des resolvers vérifie context[:current_user] et lève une GraphQL::ExecutionError en cas d'accès non autorisé. Le guide d'autorisation de graphql-ruby détaille les patterns d'autorisation au niveau des champs et des types.
Mises à Jour en Temps Réel avec les Subscriptions GraphQL et ActionCable
Les subscriptions permettent aux clients de recevoir des mises à jour lorsque des événements côté serveur se produisent. Rails ActionCable gère la connexion WebSocket, et graphql-ruby s'intègre via GraphQL::Subscriptions::ActionCableSubscriptions.
# app/graphql/types/subscription_type.rb
module Types
class SubscriptionType < Types::BaseObject
field :post_created, Types::PostType, null: false do
argument :user_id, ID, required: false
end
def post_created(user_id: nil)
object # L'objet passé depuis le trigger
end
end
endLe déclenchement d'une subscription peut se faire depuis n'importe où dans l'application, généralement dans un callback de modèle ou un service object.
# app/models/post.rb
class Post < ApplicationRecord
belongs_to :user
after_create_commit :notify_subscribers
private
def notify_subscribers
SharpskillSchema.subscriptions.trigger(
:post_created,
{ user_id: user_id },
self
)
end
endLe channel ActionCable qui gère les subscriptions GraphQL est fourni avec la gem.
# app/channels/graphql_channel.rb
class GraphqlChannel < ApplicationCable::Channel
def subscribed
@subscription_ids = []
end
def execute(data)
result = SharpskillSchema.execute(
data['query'],
variables: data['variables'],
context: { current_user: current_user, channel: self },
operation_name: data['operationName']
)
payload = { result: result.to_h, more: result.subscription? }
@subscription_ids << result.context[:subscription_id] if result.subscription?
transmit(payload)
end
def unsubscribed
@subscription_ids.each do |sid|
SharpskillSchema.subscriptions.delete_subscription(sid)
end
end
endCôté client, des bibliothèques comme Apollo Client ou urql se connectent au WebSocket ActionCable et gèrent l'état des subscriptions. Pour la préparation aux entretiens sur les mécanismes d'ActionCable, consulter le module ActionCable & WebSockets.
Test des APIs GraphQL avec RSpec
Tester une API GraphQL nécessite d'exécuter des queries contre le schéma et d'asserter sur la structure et les données de la réponse.
# spec/graphql/queries/users_spec.rb
RSpec.describe 'Users query' do
let!(:user) { create(:user, email: 'test@example.com') }
let(:query) do
<<~GRAPHQL
query {
users(emailContains: "test") {
id
email
}
}
GRAPHQL
end
it 'returns users matching the filter' do
result = SharpskillSchema.execute(query)
users = result.dig('data', 'users')
expect(users.length).to eq(1)
expect(users.first['email']).to eq('test@example.com')
end
endLes tests de mutations vérifient à la fois les chemins de succès et d'erreurs de validation.
# spec/graphql/mutations/create_post_spec.rb
RSpec.describe Mutations::CreatePost do
let(:user) { create(:user) }
let(:context) { { current_user: user } }
let(:mutation) do
<<~GRAPHQL
mutation($title: String!, $body: String!) {
createPost(input: { title: $title, body: $body }) {
post { id title }
errors
}
}
GRAPHQL
end
it 'creates a post when valid' do
result = SharpskillSchema.execute(
mutation,
variables: { title: 'Hello', body: 'World' },
context: context
)
data = result.dig('data', 'createPost')
expect(data['errors']).to be_empty
expect(data['post']['title']).to eq('Hello')
end
it 'returns errors when invalid' do
result = SharpskillSchema.execute(
mutation,
variables: { title: '', body: 'World' },
context: context
)
data = result.dig('data', 'createPost')
expect(data['errors']).to include("Title can't be blank")
end
endPour en savoir plus sur les patterns de test Rails, consulter le module Test avec RSpec.
Questions d'Entretien GraphQL Courantes pour les Développeurs Rails
Les recruteurs testant les connaissances GraphQL dans un contexte Rails se concentrent souvent sur des préoccupations d'implémentation pratiques plutôt que sur la théorie abstraite des types.
Comment prévenir les requêtes N+1 dans graphql-ruby ?
Utiliser DataLoader avec des sources personnalisées. Définir une classe source qui regroupe les appels à la base de données, et appeler dataloader.with(SourceClass, args).load(id) dans les resolvers. DataLoader collecte tous les IDs demandés dans une seule exécution GraphQL et les récupère en une seule requête.
Quelle est la différence entre une query et une mutation ?
Sémantique : les queries lisent des données et ne doivent pas avoir d'effets secondaires. Les mutations modifient des données et peuvent avoir des effets secondaires. GraphQL garantit une exécution parallèle pour les champs de query et une exécution séquentielle pour les champs de mutation, donc deux mutations dans la même requête s'exécutent dans l'ordre.
Comment fonctionnent les subscriptions dans Rails ?
Les subscriptions utilisent ActionCable pour les connexions WebSocket. Le client envoie une query de subscription via le WebSocket. Le serveur stocke la subscription et, quand trigger est appelé, pousse le résultat vers tous les clients correspondants. L'ID de subscription permet aux clients de se désabonner.
Quand choisir GraphQL plutôt que REST pour une API Rails ?
GraphQL réduit la sur-récupération quand les clients ont besoin de différents sous-ensembles de données, ce qui est courant dans les applications mobiles avec des tailles d'écran variables. Il élimine également le besoin de multiples endpoints REST quand une seule vue nécessite des données de plusieurs modèles. REST reste plus simple pour les APIs CRUD avec des formes de données cohérentes et pour les APIs publiques où le caching est critique.
Comment gérer l'authentification et l'autorisation dans graphql-ruby ?
L'authentification se fait dans le contrôleur avant l'exécution GraphQL, généralement via Devise, Warden ou la vérification JWT. L'utilisateur authentifié est passé dans context. L'autorisation se fait dans les resolvers ou via le hook authorized? de graphql-ruby sur les types et mutations, levant une GraphQL::ExecutionError quand l'accès est refusé.
Pour plus de préparation aux entretiens Ruby on Rails, consulter le module Rails API Mode, qui couvre les patterns de conception d'API REST qui complètent les connaissances GraphQL.
Passe à la pratique !
Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.
Construction d'APIs GraphQL Production avec Rails : Points Clés
- Installer graphql-ruby avec
rails generate graphql:installpour créer le schéma, les types et le routage - Utiliser les sources DataLoader pour toutes les associations afin de regrouper les requêtes de base de données et prévenir les problèmes de performance N+1
- Définir les mutations dans des classes séparées avec des champs d'erreur explicites dans le type de retour
- Intégrer les subscriptions via ActionCable pour les mises à jour en temps réel, en déclenchant depuis les callbacks de modèle ou les service objects
- Tester les queries et mutations en les exécutant directement contre le schéma dans RSpec
- L'authentification appartient au contrôleur ; les vérifications d'autorisation se font à l'intérieur des resolvers en utilisant
context[:current_user] - Choisir GraphQL quand les clients ont besoin d'une récupération de données flexible ; rester sur REST pour les APIs CRUD simples avec des réponses uniformes
Tu saurais repérer le bug en Ruby on Rails ?
Un vrai bout de code, un bug caché, une tentative par jour. Sans compte pour essayer.

Écrit par
Anthony Fillion-MailletFondateur de SharpSkill
Développeur fullstack depuis plus de 10 ans. Il dirige SharpSkill et répond de tout ce qui y est publié.
Mis à jour le 19 septembre 2026
Partager
Articles similaires

Jobs en arrière-plan Rails en 2026 : Sidekiq vs Good Job et questions d'entretien
Comparaison approfondie entre Sidekiq, Good Job et Solid Queue pour les tâches asynchrones Rails, avec les questions techniques posées en entretien.

Rails Active Storage en 2026 : Upload de fichiers, intégration S3 et questions d'entretien
Guide complet sur Rails Active Storage en 2026 : configuration des uploads de fichiers, intégration AWS S3, variantes d'images et questions techniques pour préparer les entretiens Ruby on Rails.

Rails Stimulus et Importmaps en 2026 : JavaScript Moderne Sans Outils de Build
Guide complet sur Rails Stimulus et Importmaps en 2026. Découvrez comment créer des applications JavaScript modernes sans webpack ni bundlers complexes avec Hotwire.