Rails GraphQL API in 2026: graphql-ruby, Subscriptions en Sollicitatievragen

Een productieklare GraphQL API bouwen met Rails 8 en graphql-ruby. Schema-ontwerp, mutations, subscriptions met ActionCable en voorbereiding op sollicitatiegesprekken.

Rails GraphQL API in 2026: graphql-ruby, Subscriptions en Sollicitatievragen

GraphQL is de standaardkeuze geworden voor API's die flexibele data-ophaling nodig hebben, en graphql-ruby brengt deze functionaliteit naar Rails met nauwe ActiveRecord-integratie. Rails 8, uitgebracht eind 2024, werkt uitstekend samen met graphql-ruby 2.4, dat verbeterde subscription-afhandeling en betere lazy execution voor N+1-preventie introduceerde.

Wat graphql-ruby naar Rails brengt

De graphql-ruby gem biedt een complete GraphQL-implementatie: schema-definitie met een Ruby DSL, automatische type-inferentie uit ActiveRecord-modellen, ingebouwde DataLoader voor batching en ActionCable-integratie voor realtime subscriptions.

graphql-ruby Instellen in een Rails 8 Applicatie

De gem-generator maakt de initiële schemastructuur aan en koppelt het GraphQL-endpoint. Begin met het toevoegen van de gem en voer de install-generator uit om de basisbestanden te creëren.

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

De generator maakt app/graphql/ aan met het schemabestand, basistypes en een mutations-directory. Het voegt ook een route toe op /graphql en koppelt optioneel GraphiQL voor ontwikkeling.

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

Het schemabestand fungeert als toegangspunt. Het declareert welke types queries, mutations en subscriptions afhandelen, en registreert middleware zoals DataLoader.

Types en Resolvers Definiëren voor Rails-Modellen

Elk ActiveRecord-model dat in de API verschijnt, heeft een corresponderend GraphQL-type nodig. Het type definieert welke velden worden blootgesteld en hoe ze worden opgelost.

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

De dataloader.with-aanroep bundelt meerdere posts-ophalingen in één SQL-query. Zonder DataLoader zou een query die 50 gebruikers met hun posts opvraagt 51 queries uitvoeren. Met DataLoader zijn het er 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

Dit patroon verschijnt in elk serieus graphql-ruby-project. De officiële DataLoader-documentatie behandelt aanvullende use cases zoals caching en geneste sources.

Queries Bouwen met Argumenten en Filtering

Het QueryType definieert de root-velden die beschikbaar zijn voor clients. Argumenten maken filtering en paginering mogelijk.

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

Resolver-methoden ontvangen keyword-argumenten die overeenkomen met de velddefinitie. Het retourneren van nil uit een nullable veld is geldig; het gooien van een error uit een non-null veld triggert een GraphQL-foutrespons.

Klaar om je Ruby on Rails gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

Mutations: Records Aanmaken en Bijwerken

Mutations volgen een striktere conventie dan queries. Elke mutation leeft in zijn eigen klasse en retourneert een payload-type dat zowel het resultaat als eventuele fouten bevat.

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

De context[:current_user] komt uit de controller. Authenticatie gebeurt vóór de GraphQL-laag, meestal met Devise of een JWT-bibliotheek.

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

Toegangsbeheer binnen resolvers controleert context[:current_user] en gooit GraphQL::ExecutionError wanneer niet geautoriseerd. De graphql-ruby autorisatiegids beschrijft veld-niveau en type-niveau autorisatiepatronen in detail.

Realtime Updates met GraphQL Subscriptions en ActionCable

Subscriptions stellen clients in staat om updates te ontvangen wanneer server-side events plaatsvinden. Rails ActionCable handelt de WebSocket-verbinding af, en graphql-ruby integreert via 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

Het triggeren van een subscription gebeurt vanuit elke plek in de applicatie, meestal in een model-callback of 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

Het ActionCable-channel dat GraphQL-subscriptions afhandelt wordt met de gem meegeleverd.

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

Aan de client-kant verbinden bibliotheken zoals Apollo Client of urql met de ActionCable WebSocket en beheren de subscription-status. Voor sollicitatievoorbereidng over ActionCable-mechanismen, zie de ActionCable & WebSockets module.

GraphQL API's Testen met RSpec

Het testen van een GraphQL API vereist het uitvoeren van queries tegen het schema en het verifiëren van de responsstructuur en data.

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

Mutation-tests verifiëren zowel succes- als validatiefout-paden.

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

Voor meer Rails-testpatronen, zie de Testing with RSpec module.

Veelvoorkomende GraphQL Sollicitatievragen voor Rails-Ontwikkelaars

Interviewers die GraphQL-kennis testen in een Rails-context richten zich vaak op praktische implementatiezaken in plaats van abstracte type-theorie.

Hoe voorkom je N+1-queries in graphql-ruby?

Met DataLoader en custom sources. Definieer een source-klasse die database-aanroepen bundelt, en roep dataloader.with(SourceClass, args).load(id) aan in resolvers. DataLoader verzamelt alle ID's die in een enkele GraphQL-uitvoering worden opgevraagd en haalt ze op in één query.

Wat is het verschil tussen een query en een mutation?

Semantiek: queries lezen data en mogen geen bijeffecten hebben. Mutations wijzigen data en kunnen bijeffecten hebben. GraphQL garandeert parallelle uitvoering voor query-velden en sequentiële uitvoering voor mutation-velden, dus twee mutations in hetzelfde verzoek worden op volgorde uitgevoerd.

Hoe werken subscriptions in Rails?

Subscriptions gebruiken ActionCable voor WebSocket-verbindingen. De client stuurt een subscription-query via het WebSocket. De server slaat de subscription op en pusht bij aanroep van trigger het resultaat naar alle overeenkomende clients. De subscription-ID stelt clients in staat om zich af te melden.

Wanneer zou je GraphQL boven REST kiezen voor een Rails API?

GraphQL vermindert over-fetching wanneer clients verschillende subsets van data nodig hebben, wat gebruikelijk is bij mobiele apps met variërende schermgroottes. Het elimineert ook de noodzaak voor meerdere REST-endpoints wanneer een enkele view data van meerdere modellen vereist. REST blijft eenvoudiger voor CRUD-zware API's met consistente datastructuren en voor publieke API's waar caching kritiek is.

Hoe ga je om met authenticatie en autorisatie in graphql-ruby?

Authenticatie gebeurt in de controller vóór GraphQL-uitvoering, meestal via Devise, Warden of JWT-verificatie. De geauthenticeerde gebruiker wordt doorgegeven in context. Autorisatie gebeurt in resolvers of via de authorized?-hook van graphql-ruby op types en mutations, waarbij GraphQL::ExecutionError wordt gegooid wanneer toegang wordt geweigerd.

Voor meer Ruby on Rails sollicitatievoorbereiding, zie de Rails API Mode module, die REST API-ontwerppatronen behandelt die GraphQL-kennis aanvullen.

Begin met oefenen!

Test je kennis met onze gespreksimulatoren en technische tests.

Productie GraphQL API's Bouwen met Rails: Kernpunten

  • Installeer graphql-ruby met rails generate graphql:install om schema, types en routing te scaffolden
  • Gebruik DataLoader sources voor alle associaties om database-queries te bundelen en N+1-performanceproblemen te voorkomen
  • Definieer mutations in aparte klassen met expliciete foutvelden in het returntype
  • Integreer subscriptions via ActionCable voor realtime updates, getriggerd vanuit model-callbacks of service-objecten
  • Test queries en mutations door ze direct tegen het schema uit te voeren in RSpec
  • Authenticatie hoort in de controller; autorisatiecontroles gebeuren binnen resolvers met context[:current_user]
  • Kies GraphQL wanneer clients flexibele data-ophaling nodig hebben; blijf bij REST voor simpele CRUD API's met uniforme responses
Dagelijkse challenge

Zie jij de bug in Ruby on Rails?

Een echt codefragment, een verborgen bug, één poging per dag. Zonder account uit te proberen.

Anthony Fillion-Maillet

Geschreven door

Anthony Fillion-Maillet

Oprichter van SharpSkill

Al meer dan 10 jaar fullstack-ontwikkelaar. Hij leidt SharpSkill en staat in voor alles wat hier verschijnt.

Bijgewerkt op 19 september 2026

Delen

Gerelateerde artikelen