Rails GraphQL API 2026年版: graphql-ruby、サブスクリプション、面接対策質問集
graphql-rubyを使用したRails GraphQL APIの構築方法を解説。DataLoaderによるN+1対策、ActionCableサブスクリプション、RSpecテスト、実践的な面接質問を網羅的にカバーします。

GraphQLは柔軟なデータ取得が必要なAPIにおいて標準的な選択肢となっており、graphql-rubyはActiveRecordとの緊密な連携によりこの機能をRailsにもたらします。2024年末にリリースされたRails 8は、改良されたサブスクリプション処理とN+1防止のための遅延実行機能を導入したgraphql-ruby 2.4と相性が良いです。
graphql-ruby gemは完全なGraphQL実装を提供します。Ruby DSLによるスキーマ定義、ActiveRecordモデルからの自動型推論、バッチ処理のための組み込みDataLoader、リアルタイムサブスクリプション用のActionCable連携が含まれます。
Rails 8アプリケーションでのgraphql-rubyセットアップ
gem generatorは初期スキーマ構造を作成し、GraphQLエンドポイントをマウントします。gemを追加してinstall generatorを実行し、ベースファイルをスキャフォールドします。
# Gemfile
gem 'graphql', '~> 2.4'# Terminal
bundle install
rails generate graphql:installgeneratorはapp/graphql/にスキーマファイル、ベースタイプ、mutationsディレクトリを作成します。また、/graphqlにルートを追加し、開発環境ではオプションでGraphiQLをマウントします。
# app/graphql/sharpskill_schema.rb
class SharpskillSchema < GraphQL::Schema
mutation(Types::MutationType)
query(Types::QueryType)
subscription(Types::SubscriptionType)
# N+1防止のための組み込みDataLoaderを使用
use GraphQL::Dataloader
# サブスクリプションに必要
use GraphQL::Subscriptions::ActionCableSubscriptions
endスキーマファイルはエントリーポイントとして機能します。クエリ、ミューテーション、サブスクリプションを処理するタイプを宣言し、DataLoaderなどのミドルウェアを登録します。
Railsモデル用のタイプとリゾルバーの定義
APIに登場する各ActiveRecordモデルには対応するGraphQLタイプが必要です。タイプはどのフィールドが公開され、どのように解決されるかを定義します。
# 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
# DataLoaderによる自動バッチ処理を行う関連
field :posts, [Types::PostType], null: false
def posts
dataloader.with(Sources::ActiveRecordCollection, Post, :user_id).load(object.id)
end
end
enddataloader.with呼び出しは複数のpostsフェッチを単一のSQLクエリにバッチ処理します。DataLoaderがなければ、50人のユーザーとその投稿を要求するクエリは51回のクエリを実行します。DataLoaderを使えば、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
endこのパターンはすべての本格的なgraphql-rubyプロジェクトで見られます。公式DataLoaderドキュメントではキャッシュやネストされたソースなどの追加ユースケースがカバーされています。
引数とフィルタリングを使ったクエリの構築
QueryTypeはクライアントが利用可能なルートフィールドを定義します。引数によりフィルタリングとページネーションが可能になります。
# 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リゾルバーメソッドはフィールド定義に一致するキーワード引数を受け取ります。nullable フィールドからはnilを返すことが有効です。non-nullフィールドからエラーを発生させるとGraphQLエラーレスポンスがトリガーされます。
Ruby on Railsの面接対策はできていますか?
インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。
ミューテーション: レコードの作成と更新
ミューテーションはクエリより厳格な規約に従います。各ミューテーションは独自のクラスに存在し、結果とエラーの両方を含むペイロードタイプを返します。
# 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
endcontext[:current_user]はコントローラーから取得されます。認証はGraphQLレイヤーの前に行われ、通常Deviseや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
endリゾルバー内のアクセス制御はcontext[:current_user]をチェックし、権限がない場合はGraphQL::ExecutionErrorを発生させます。graphql-ruby認可ガイドではフィールドレベルとタイプレベルの認可パターンが詳述されています。
GraphQLサブスクリプションとActionCableによるリアルタイム更新
サブスクリプションにより、クライアントはサーバーサイドのイベント発生時に更新を受け取ることができます。Rails ActionCableがWebSocket接続を処理し、graphql-rubyは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 # triggerから渡されるオブジェクト
end
end
endサブスクリプションのトリガーはアプリケーションの任意の場所から行えます。通常、モデルのコールバックやサービスオブジェクト内で実行されます。
# 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
endGraphQLサブスクリプションを処理するActionCableチャンネルは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
endクライアント側では、Apollo ClientやurqlなどのライブラリがActionCable WebSocketに接続し、サブスクリプション状態を管理します。ActionCableの仕組みについての面接対策は、ActionCable & WebSocketsモジュールを参照してください。
RSpecによるGraphQL APIのテスト
GraphQL APIのテストでは、スキーマに対してクエリを実行し、レスポンスの構造とデータについてアサーションを行います。
# 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ミューテーションのテストでは、成功とバリデーションエラーの両方のパスを検証します。
# 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
endRailsのテストパターンについては、RSpecテストモジュールを参照してください。
Rails開発者向けGraphQL面接頻出質問
Rails環境でのGraphQL知識を問う面接官は、抽象的な型理論よりも実践的な実装上の懸念に焦点を当てることが多いです。
graphql-rubyでN+1クエリを防ぐにはどうしますか?
カスタムソースを持つDataLoaderを使用します。データベース呼び出しをバッチ処理するソースクラスを定義し、リゾルバー内でdataloader.with(SourceClass, args).load(id)を呼び出します。DataLoaderは単一のGraphQL実行で要求されたすべてのIDを収集し、1回のクエリでフェッチします。
クエリとミューテーションの違いは何ですか?
意味論の違いです。クエリはデータを読み取り、副作用を持つべきではありません。ミューテーションはデータを変更し、副作用を持つ可能性があります。GraphQLはクエリフィールドの並列実行とミューテーションフィールドの順次実行を保証するため、同じリクエスト内の2つのミューテーションは順番に実行されます。
Railsでサブスクリプションはどのように機能しますか?
サブスクリプションはWebSocket接続にActionCableを使用します。クライアントはWebSocketを通じてサブスクリプションクエリを送信します。サーバーはサブスクリプションを保存し、triggerが呼び出されると、一致するすべてのクライアントに結果をプッシュします。サブスクリプションIDによりクライアントは購読解除できます。
Rails APIでRESTではなくGraphQLを選択するのはどのような場合ですか?
GraphQLは、クライアントが異なるデータのサブセットを必要とする場合(さまざまな画面サイズを持つモバイルアプリで一般的)、オーバーフェッチを削減します。また、単一のビューが複数のモデルからのデータを必要とする場合、複数のRESTエンドポイントを不要にします。RESTは、一貫したデータ形状を持つCRUD重視のAPIや、キャッシュが重要なパブリックAPIでは依然としてシンプルです。
graphql-rubyで認証と認可をどのように扱いますか?
認証はGraphQL実行前にコントローラーで行われ、通常Devise、Warden、またはJWT検証を使用します。認証されたユーザーはcontextで渡されます。認可はリゾルバー内またはgraphql-rubyのタイプとミューテーションのauthorized?フックを通じて行われ、アクセスが拒否された場合はGraphQL::ExecutionErrorを発生させます。
Ruby on Rails面接対策については、GraphQLの知識を補完するRESTful APIデザインパターンをカバーするRails APIモードモジュールを参照してください。
今すぐ練習を始めましょう!
面接シミュレーターと技術テストで知識をテストしましょう。
RailsでプロダクションGraphQL APIを構築する: 重要なポイント
rails generate graphql:installでgraphql-rubyをインストールし、スキーマ、タイプ、ルーティングをスキャフォールドする- すべての関連にDataLoaderソースを使用し、データベースクエリをバッチ処理してN+1パフォーマンス問題を防ぐ
- ミューテーションは戻り値の型に明示的なエラーフィールドを持つ別々のクラスで定義する
- ActionCableを通じてサブスクリプションを連携させ、モデルコールバックやサービスオブジェクトからトリガーしてリアルタイム更新を実現する
- RSpecでスキーマに対して直接クエリとミューテーションを実行してテストする
- 認証はコントローラーで行い、認可チェックは
context[:current_user]を使用してリゾルバー内で行う - クライアントが柔軟なデータ取得を必要とする場合はGraphQLを選択し、均一なレスポンスを持つシンプルなCRUD APIではRESTを維持する
Ruby on Rails のバグを見つけられますか
実際のコード、隠れたバグ、1日1回。アカウントなしで試せます。

執筆
Anthony Fillion-MailletSharpSkill 創業者
10 年以上フルスタック開発に携わっています。SharpSkill を運営し、ここで公開される内容に責任を負っています。
2026年9月19日 更新
タグ
共有
関連記事

Rails 8のSolid QueueとSolid Cache:2026年技術面接完全ガイド
Rails 8で標準搭載されたSolid QueueとSolid CacheがRedisを置き換える仕組みを解説。アーキテクチャ、設定、同時実行制御、2026年の面接頻出質問まで網羅。

Ruby on Rails 面接質問: 2026年トップ25
Ruby on Railsで最も多く問われる面接質問25選。MVCアーキテクチャ、Active Record、マイグレーション、RSpecテスト、REST APIを詳細な解説とコード例とともに解説。

Rails バックグラウンドジョブ 2026年版:Sidekiq vs Good Job の徹底比較と面接対策
2026年のRailsにおけるバックグラウンドジョブ処理を徹底解説。Sidekiq、Good Job、Solid Queueの特徴を比較し、技術面接でよく出題される質問と回答例を紹介します。