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.

Rails GraphQL API nel 2026: graphql-ruby, Subscriptions e Domande da Colloquio

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.

Cosa offre graphql-ruby a Rails

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.

ruby
# Gemfile
gem 'graphql', '~> 2.4'
bash
# Terminal
bundle install
rails generate graphql:install

Il 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.

ruby
# 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
end

Il 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.

ruby
# 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
end

La 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.

ruby
# 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
end

Questo 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.

ruby
# 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
end

I 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.

ruby
# 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
end

Il context[:current_user] proviene dal controller. L'autenticazione avviene prima del layer GraphQL, tipicamente usando Devise o una libreria JWT.

ruby
# 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
end

Il 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.

ruby
# 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
end

L'attivazione di una subscription avviene da qualsiasi punto dell'applicazione, tipicamente in un callback del modello o service object.

ruby
# 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
end

Il channel ActionCable che gestisce le subscription GraphQL viene fornito con la gem.

ruby
# 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
end

Lato 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.

ruby
# 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
end

I test delle mutation verificano sia i percorsi di successo che di errore di validazione.

ruby
# 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
end

Per 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:install per 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
Sfida del giorno

Sapresti trovare il bug in Ruby on Rails?

Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Anthony Fillion-Maillet

Scritto da

Anthony Fillion-Maillet

Fondatore 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