Rails GraphQL API nel 2026: graphql-ruby, Subscriptions e Domande da Colloquio
Costruire un'API GraphQL production-ready con Rails 8 e graphql-ruby. Design dello schema, mutations, subscriptions con ActionCable e preparazione ai colloqui.

GraphQL si è affermato come scelta predefinita per le API che richiedono recupero dati flessibile, e graphql-ruby porta questa funzionalità a Rails con una stretta integrazione ActiveRecord. Rails 8, rilasciato alla fine del 2024, si abbina perfettamente con graphql-ruby 2.4, che ha introdotto una gestione migliorata delle subscription e una migliore lazy execution per la prevenzione degli N+1.
La gem graphql-ruby fornisce un'implementazione GraphQL completa: definizione dello schema con una DSL Ruby, inferenza automatica dei tipi dai modelli ActiveRecord, DataLoader integrato per il batching e integrazione ActionCable per le subscription in tempo reale.
Configurazione di graphql-ruby in un'Applicazione Rails 8
Il generatore della gem crea la struttura iniziale dello schema e monta l'endpoint GraphQL. Si inizia aggiungendo la gem e eseguendo il generatore install per creare i file di base.
# Gemfile
gem 'graphql', '~> 2.4'# Terminal
bundle install
rails generate graphql:installIl generatore crea app/graphql/ con il file dello schema, i tipi base e una directory mutations. Aggiunge anche una route su /graphql e opzionalmente monta GraphiQL per lo sviluppo.
# app/graphql/sharpskill_schema.rb
class SharpskillSchema < GraphQL::Schema
mutation(Types::MutationType)
query(Types::QueryType)
subscription(Types::SubscriptionType)
# Use the built-in DataLoader for N+1 prevention
use GraphQL::Dataloader
# Required for subscriptions
use GraphQL::Subscriptions::ActionCableSubscriptions
endIl file dello schema funge da punto di ingresso. Dichiara quali tipi gestiscono query, mutation e subscription, e registra qualsiasi middleware come DataLoader.
Definizione di Types e Resolvers per i Modelli Rails
Ogni modello ActiveRecord che appare nell'API necessita di un tipo GraphQL corrispondente. Il tipo definisce quali campi sono esposti e come vengono risolti.
# 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 with automatic batching via DataLoader
field :posts, [Types::PostType], null: false
def posts
dataloader.with(Sources::ActiveRecordCollection, Post, :user_id).load(object.id)
end
end
endLa chiamata dataloader.with raggruppa multiple richieste posts in una singola query SQL. Senza DataLoader, una query che richiede 50 utenti con i loro post eseguirebbe 51 query. Con DataLoader, ne esegue 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
endQuesto pattern appare in ogni progetto graphql-ruby serio. La documentazione ufficiale DataLoader copre casi d'uso aggiuntivi come caching e source annidate.
Costruzione di Query con Argomenti e Filtri
Il QueryType definisce i campi root disponibili ai client. Gli argomenti permettono filtraggio e paginazione.
# 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
endI metodi resolver ricevono argomenti keyword che corrispondono alla definizione del campo. Restituire nil da un campo nullable è valido; lanciare un errore da un campo non-null genera una risposta di errore GraphQL.
Pronto a superare i tuoi colloqui su Ruby on Rails?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
Mutations: Creazione e Aggiornamento dei Record
Le mutations seguono una convenzione più rigida delle query. Ogni mutation risiede nella propria classe e restituisce un tipo payload che include sia il risultato che eventuali errori.
# 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
endIl context[:current_user] proviene dal controller. L'autenticazione avviene prima del layer GraphQL, tipicamente usando Devise o una libreria 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
endIl controllo degli accessi nei resolver verifica context[:current_user] e lancia GraphQL::ExecutionError quando non autorizzato. La guida autorizzazione graphql-ruby descrive in dettaglio i pattern di autorizzazione a livello di campo e tipo.
Aggiornamenti in Tempo Reale con GraphQL Subscriptions e ActionCable
Le subscription permettono ai client di ricevere aggiornamenti quando si verificano eventi lato server. Rails ActionCable gestisce la connessione WebSocket, e graphql-ruby si integra tramite 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 # The object passed from trigger
end
end
endL'attivazione di una subscription avviene da qualsiasi punto dell'applicazione, tipicamente in un callback del modello o 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
endIl channel ActionCable che gestisce le subscription GraphQL viene fornito con 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
endLato client, librerie come Apollo Client o urql si connettono al WebSocket ActionCable e gestiscono lo stato delle subscription. Per la preparazione ai colloqui sui meccanismi ActionCable, vedere il modulo ActionCable & WebSockets.
Testing delle API GraphQL con RSpec
Testare un'API GraphQL richiede l'esecuzione di query contro lo schema e la verifica della struttura e dei dati della risposta.
# 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
endI test delle mutation verificano sia i percorsi di successo che di errore di validazione.
# 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
endPer ulteriori pattern di testing Rails, vedere il modulo Testing with RSpec.
Domande Comuni da Colloquio GraphQL per Sviluppatori Rails
Gli intervistatori che testano la conoscenza di GraphQL nel contesto Rails spesso si concentrano su problemi pratici di implementazione piuttosto che sulla teoria astratta dei tipi.
Come si prevengono le query N+1 in graphql-ruby?
Usando DataLoader con source personalizzate. Si definisce una classe source che raggruppa le chiamate al database, e si chiama dataloader.with(SourceClass, args).load(id) nei resolver. DataLoader raccoglie tutti gli ID richiesti in una singola esecuzione GraphQL e li recupera in una query.
Qual è la differenza tra una query e una mutation?
La semantica: le query leggono dati e non dovrebbero avere effetti collaterali. Le mutation modificano dati e possono avere effetti collaterali. GraphQL garantisce l'esecuzione parallela per i campi query e l'esecuzione sequenziale per i campi mutation, quindi due mutation nella stessa richiesta vengono eseguite in ordine.
Come funzionano le subscription in Rails?
Le subscription usano ActionCable per le connessioni WebSocket. Il client invia una query subscription attraverso il WebSocket. Il server memorizza la subscription e, quando viene chiamato trigger, invia il risultato a tutti i client corrispondenti. L'ID della subscription permette ai client di annullare l'iscrizione.
Quando si sceglierebbe GraphQL rispetto a REST per un'API Rails?
GraphQL riduce l'over-fetching quando i client necessitano di sottoinsiemi diversi di dati, comune nelle app mobile con dimensioni di schermo variabili. Elimina anche la necessità di endpoint REST multipli quando una singola vista richiede dati da diversi modelli. REST rimane più semplice per API CRUD-intensive con forme di dati consistenti e per API pubbliche dove il caching è critico.
Come si gestiscono autenticazione e autorizzazione in graphql-ruby?
L'autenticazione avviene nel controller prima dell'esecuzione GraphQL, tipicamente tramite Devise, Warden o verifica JWT. L'utente autenticato viene passato nel context. L'autorizzazione avviene nei resolver o tramite l'hook authorized? di graphql-ruby su tipi e mutation, lanciando GraphQL::ExecutionError quando l'accesso è negato.
Per ulteriore preparazione ai colloqui Ruby on Rails, vedere il modulo Rails API Mode, che copre pattern di design API REST che completano la conoscenza di GraphQL.
Inizia a praticare!
Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.
Costruire API GraphQL di Produzione con Rails: Punti Chiave
- Installare graphql-ruby con
rails generate graphql:installper creare schema, tipi e routing - Usare source DataLoader per tutte le associazioni per raggruppare le query al database e prevenire problemi di performance N+1
- Definire le mutation in classi separate con campi di errore espliciti nel tipo di ritorno
- Integrare le subscription via ActionCable per aggiornamenti in tempo reale, attivate da callback del modello o service object
- Testare query e mutation eseguendole direttamente contro lo schema in RSpec
- L'autenticazione appartiene al controller; i controlli di autorizzazione avvengono nei resolver usando
context[:current_user] - Scegliere GraphQL quando i client necessitano di recupero dati flessibile; rimanere su REST per semplici API CRUD con risposte uniformi
Sapresti trovare il bug in Ruby on Rails?
Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Scritto da
Anthony Fillion-MailletFondatore di SharpSkill
Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.
Aggiornato il 19 settembre 2026
Condividi
Articoli correlati

Background Jobs in Rails 2026: Sidekiq vs Good Job con Domande da Colloquio
Confronto completo tra Sidekiq, Good Job e Solid Queue per i background jobs in Rails. Guida alla scelta della soluzione ottimale con domande tecniche per colloqui.

Rails Active Storage nel 2026: Upload di File, Integrazione S3 e Domande per Colloqui
Padroneggiare Rails Active Storage per upload di file con S3 e direct upload. Tutorial completo con esempi di codice e domande frequenti nei colloqui sulla gestione dei file in Ruby on Rails.

Rails Stimulus e Importmaps nel 2026: JavaScript Moderno Senza Build Tools
Scopri come Rails Stimulus e Importmaps permettono lo sviluppo JavaScript senza webpack o esbuild. Tutorial completo con controller, Outlets API e tecniche di debugging.