Rails GraphQL API 2026: graphql-ruby, Subscriptions und Interview-Fragen
Erstellen einer produktionsreifen GraphQL-API mit Rails 8 und graphql-ruby. Schema-Design, Mutations, Subscriptions mit ActionCable und Interview-Vorbereitung.

GraphQL hat sich als Standardwahl für APIs etabliert, die flexible Datenabfragen benötigen, und graphql-ruby bringt diese Funktionalität mit enger ActiveRecord-Integration zu Rails. Rails 8, veröffentlicht Ende 2024, harmoniert hervorragend mit graphql-ruby 2.4, das eine verbesserte Subscription-Handhabung und bessere Lazy Execution zur N+1-Prävention einführte.
Das graphql-ruby Gem bietet eine vollständige GraphQL-Implementierung: Schema-Definition mit einer Ruby-DSL, automatische Typinferenz aus ActiveRecord-Modellen, integrierter DataLoader für Batching und ActionCable-Integration für Echtzeit-Subscriptions.
Einrichtung von graphql-ruby in einer Rails 8 Anwendung
Der Gem-Generator erstellt die initiale Schema-Struktur und bindet den GraphQL-Endpunkt ein. Zunächst wird das Gem hinzugefügt und der Install-Generator ausgeführt, um die Basisdateien zu erstellen.
# Gemfile
gem 'graphql', '~> 2.4'# Terminal
bundle install
rails generate graphql:installDer Generator erstellt app/graphql/ mit der Schema-Datei, Basistypen und einem Mutations-Verzeichnis. Er fügt außerdem eine Route unter /graphql hinzu und bindet optional GraphiQL für die Entwicklung ein.
# 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
endDie Schema-Datei fungiert als Einstiegspunkt. Sie deklariert, welche Typen Queries, Mutations und Subscriptions handhaben, und registriert jegliche Middleware wie DataLoader.
Definition von Types und Resolvers für Rails-Modelle
Jedes ActiveRecord-Modell, das in der API erscheint, benötigt einen entsprechenden GraphQL-Typ. Der Typ definiert, welche Felder exponiert werden und wie sie aufgelöst werden.
# 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
endDer dataloader.with-Aufruf fasst mehrere posts-Abrufe in einer einzigen SQL-Abfrage zusammen. Ohne DataLoader würde eine Abfrage, die 50 Benutzer mit ihren Posts anfordert, 51 Queries ausführen. Mit DataLoader sind es nur 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
endDieses Muster erscheint in jedem ernsthaften graphql-ruby-Projekt. Die offizielle DataLoader-Dokumentation behandelt weitere Anwendungsfälle wie Caching und verschachtelte Sources.
Aufbau von Queries mit Argumenten und Filterung
Der QueryType definiert die Root-Felder, die Clients zur Verfügung stehen. Argumente ermöglichen Filterung und Paginierung.
# 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
endResolver-Methoden erhalten Keyword-Argumente, die der Felddefinition entsprechen. Die Rückgabe von nil aus einem nullable Feld ist gültig; das Auslösen eines Fehlers aus einem non-null Feld löst eine GraphQL-Fehlerantwort aus.
Bereit für deine Ruby on Rails-Interviews?
Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.
Mutations: Erstellen und Aktualisieren von Datensätzen
Mutations folgen einer strengeren Konvention als Queries. Jede Mutation lebt in ihrer eigenen Klasse und gibt einen Payload-Typ zurück, der sowohl das Ergebnis als auch eventuelle Fehler enthält.
# 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
endDer context[:current_user] kommt vom Controller. Die Authentifizierung erfolgt vor der GraphQL-Schicht, typischerweise mit Devise oder einer JWT-Bibliothek.
# 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
endDie Zugriffskontrolle innerhalb von Resolvern prüft context[:current_user] und löst GraphQL::ExecutionError bei fehlender Autorisierung aus. Der graphql-ruby Authorization Guide beschreibt detailliert Feld- und Typ-Level-Autorisierungsmuster.
Echtzeit-Updates mit GraphQL Subscriptions und ActionCable
Subscriptions ermöglichen es Clients, Updates zu erhalten, wenn serverseitige Ereignisse auftreten. Rails ActionCable handhabt die WebSocket-Verbindung, und graphql-ruby integriert sich über 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
endDas Auslösen einer Subscription kann von überall in der Anwendung erfolgen, typischerweise in einem Model-Callback oder Service-Objekt.
# 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
endDer ActionCable-Channel, der GraphQL-Subscriptions handhabt, wird mit dem Gem ausgeliefert.
# 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
endAuf der Client-Seite verbinden sich Bibliotheken wie Apollo Client oder urql mit dem ActionCable-WebSocket und verwalten den Subscription-Status. Für Interview-Vorbereitung zu ActionCable-Mechanismen siehe das ActionCable & WebSockets Modul.
Testen von GraphQL-APIs mit RSpec
Das Testen einer GraphQL-API erfordert das Ausführen von Queries gegen das Schema und das Überprüfen der Antwortstruktur und Daten.
# 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
endMutation-Tests verifizieren sowohl Erfolgs- als auch Validierungsfehler-Pfade.
# 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
endFür weitere Rails-Testmuster siehe das Testing with RSpec Modul.
Häufige GraphQL Interview-Fragen für Rails-Entwickler
Interviewer, die GraphQL-Wissen im Rails-Kontext testen, konzentrieren sich oft auf praktische Implementierungsaspekte statt auf abstrakte Typentheorie.
Wie verhindert man N+1-Queries in graphql-ruby?
Mit DataLoader und benutzerdefinierten Sources. Eine Source-Klasse wird definiert, die Datenbankaufrufe bündelt, und dataloader.with(SourceClass, args).load(id) wird in Resolvern aufgerufen. DataLoader sammelt alle IDs, die in einer einzelnen GraphQL-Ausführung angefordert werden, und ruft sie in einer Abfrage ab.
Was ist der Unterschied zwischen einer Query und einer Mutation?
Die Semantik: Queries lesen Daten und sollten keine Seiteneffekte haben. Mutations modifizieren Daten und können Seiteneffekte haben. GraphQL garantiert parallele Ausführung für Query-Felder und sequentielle Ausführung für Mutation-Felder, sodass zwei Mutations in derselben Anfrage der Reihe nach ausgeführt werden.
Wie funktionieren Subscriptions in Rails?
Subscriptions nutzen ActionCable für WebSocket-Verbindungen. Der Client sendet eine Subscription-Query über das WebSocket. Der Server speichert die Subscription und pusht bei Aufruf von trigger das Ergebnis an alle passenden Clients. Die Subscription-ID ermöglicht es Clients, sich abzumelden.
Wann würde man GraphQL gegenüber REST für eine Rails-API wählen?
GraphQL reduziert Over-Fetching, wenn Clients unterschiedliche Datenuntermengen benötigen, was bei mobilen Apps mit variierenden Bildschirmgrößen häufig vorkommt. Es eliminiert auch die Notwendigkeit mehrerer REST-Endpunkte, wenn eine einzelne Ansicht Daten von mehreren Modellen benötigt. REST bleibt einfacher für CRUD-lastige APIs mit konsistenten Datenformen und für öffentliche APIs, bei denen Caching kritisch ist.
Wie handhabt man Authentifizierung und Autorisierung in graphql-ruby?
Die Authentifizierung erfolgt im Controller vor der GraphQL-Ausführung, typischerweise über Devise, Warden oder JWT-Verifizierung. Der authentifizierte Benutzer wird im context übergeben. Die Autorisierung erfolgt in Resolvern oder über den authorized?-Hook von graphql-ruby auf Types und Mutations, wobei GraphQL::ExecutionError bei verweigertem Zugriff ausgelöst wird.
Für weitere Ruby on Rails Interview-Vorbereitung siehe das Rails API Mode Modul, das REST-API-Designmuster behandelt, die GraphQL-Wissen ergänzen.
Fang an zu üben!
Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.
Produktionsreife GraphQL-APIs mit Rails: Zusammenfassung
- graphql-ruby mit
rails generate graphql:installinstallieren, um Schema, Types und Routing zu erstellen - DataLoader Sources für alle Assoziationen verwenden, um Datenbankabfragen zu bündeln und N+1-Performance-Probleme zu verhindern
- Mutations in separaten Klassen mit expliziten Fehlerfeldern im Rückgabetyp definieren
- Subscriptions über ActionCable für Echtzeit-Updates integrieren, ausgelöst durch Model-Callbacks oder Service-Objekte
- Queries und Mutations testen, indem sie direkt gegen das Schema in RSpec ausgeführt werden
- Authentifizierung gehört in den Controller; Autorisierungsprüfungen erfolgen innerhalb der Resolver mit
context[:current_user] - GraphQL wählen, wenn Clients flexible Datenabfragen benötigen; bei einfachen CRUD-APIs mit einheitlichen Antworten bei REST bleiben
Findest du den Bug in Ruby on Rails?
Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Geschrieben von
Anthony Fillion-MailletGründer von SharpSkill
Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.
Aktualisiert am 19. September 2026
Teilen
Verwandte Artikel

Rails Background Jobs 2026: Sidekiq vs Good Job im Vergleich mit Interview-Fragen
Umfassender Vergleich von Sidekiq, Good Job und Solid Queue für Rails Background Jobs. Leitfaden zur Auswahl der richtigen Lösung mit typischen Interviewfragen.

Rails Active Storage 2026: Datei-Uploads, S3-Integration und Interview-Fragen
Rails Active Storage für Datei-Uploads mit S3 und Direct Uploads meistern. Vollständiges Tutorial mit Code-Beispielen und häufigen Interview-Fragen zur Dateiverarbeitung in Ruby on Rails.

Rails Stimulus und Importmaps 2026: Modernes JavaScript ohne Build-Tools
Entdecken Sie, wie Rails Stimulus und Importmaps JavaScript-Entwicklung ohne webpack oder esbuild ermöglichen. Tutorial mit Controllern, Outlets API und Debugging-Techniken.