Rails GraphQL API у 2026: graphql-ruby, підписки та питання на співбесіді
Повний посібник зі створення продакшн-рівня GraphQL API з Rails 8 та graphql-ruby. Проєктування схеми, мутації, підписки з ActionCable та підготовка до співбесіди.

GraphQL став стандартним вибором для API, що потребують гнучкого отримання даних, а graphql-ruby привносить цю функціональність у Rails із тісною інтеграцією з ActiveRecord. Rails 8, випущений наприкінці 2024 року, чудово поєднується з graphql-ruby 2.4, який представив покращену обробку підписок та краще ліниве виконання для запобігання проблем N+1.
Gem graphql-ruby забезпечує повну реалізацію GraphQL: визначення схеми за допомогою Ruby DSL, автоматичне виведення типів з моделей ActiveRecord, вбудований DataLoader для пакетної обробки та інтеграцію з ActionCable для підписок реального часу.
Налаштування graphql-ruby в додатку Rails 8
Генератор gem'у створює початкову структуру схеми та монтує endpoint GraphQL. Почніть з додавання gem'у та запуску інсталяційного генератора для створення базових файлів.
# Gemfile
gem 'graphql', '~> 2.4'# Terminal
bundle install
rails generate graphql:installГенератор створює каталог app/graphql/ з файлом схеми, базовими типами та директорією мутацій. Він також додає маршрут за адресою /graphql та опціонально монтує GraphiQL для розробки.
# app/graphql/sharpskill_schema.rb
class SharpskillSchema < GraphQL::Schema
mutation(Types::MutationType)
query(Types::QueryType)
subscription(Types::SubscriptionType)
# Використання вбудованого DataLoader для запобігання N+1
use GraphQL::Dataloader
# Необхідно для підписок
use GraphQL::Subscriptions::ActionCableSubscriptions
endФайл схеми виступає точкою входу. Він оголошує, які типи обробляють запити, мутації та підписки, та реєструє middleware, такий як DataLoader.
Визначення типів і резолверів для моделей Rails
Кожна модель ActiveRecord, що з'являється в API, потребує відповідного типу 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
endВиклик dataloader.with об'єднує кілька запитів постів в один 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Методи резолверів отримують іменовані аргументи, що відповідають визначенню поля. Повернення nil з nullable поля є допустимим; викидання помилки з non-null поля викликає відповідь з помилкою GraphQL.
Готовий до співбесід з Ruby on Rails?
Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.
Мутації: створення та оновлення записів
Мутації підпорядковуються суворішим конвенціям, ніж запити. Кожна мутація живе у власному класі та повертає тип payload, що містить як результат, так і можливі помилки.
# 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Запуск підписки відбувається з будь-якого місця в додатку, зазвичай у callback'у моделі або сервісному об'єкті.
# 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
endActionCable канал для обробки GraphQL підписок постачається разом з 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.
Тестування GraphQL API з RSpec
Тестування 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
endБільше про патерни тестування Rails дивіться у модулі Тестування з RSpec.
Поширені питання на співбесіді з GraphQL для Rails-розробників
Інтерв'юери, що перевіряють знання GraphQL у контексті Rails, часто фокусуються на практичних аспектах реалізації, а не на абстрактній теорії типів.
Як запобігти запитам N+1 у graphql-ruby?
Використовуйте DataLoader з власними джерелами. Визначте клас джерела, який пакетує виклики до бази даних, і викликайте dataloader.with(SourceClass, args).load(id) у резолверах. DataLoader збирає всі ID, запитані в одному виконанні GraphQL, та отримує їх одним запитом.
Яка різниця між запитом та мутацією?
Семантика: запити читають дані і не повинні мати побічних ефектів. Мутації модифікують дані і можуть мати побічні ефекти. GraphQL гарантує паралельне виконання полів запитів та послідовне виконання полів мутацій, тому дві мутації в одному запиті виконуються по черзі.
Як працюють підписки в Rails?
Підписки використовують ActionCable для WebSocket-з'єднань. Клієнт надсилає запит підписки через WebSocket. Сервер зберігає підписку і, коли викликається trigger, надсилає результат усім відповідним клієнтам. Ідентифікатор підписки дозволяє клієнтам відписатися.
Коли обрати GraphQL замість REST для Rails API?
GraphQL зменшує надмірне отримання даних, коли клієнтам потрібні різні підмножини даних, що є поширеним у мобільних додатках з різними розмірами екранів. Він також усуває потребу в кількох REST endpoint'ах, коли один view потребує даних з кількох моделей. REST залишається простішим для API з великою кількістю CRUD-операцій із консистентними формами даних та для публічних API, де кешування є критичним.
Як обробляти автентифікацію та авторизацію в graphql-ruby?
Автентифікація відбувається в контролері перед виконанням GraphQL, зазвичай через Devise, Warden або перевірку JWT. Автентифікований користувач передається в context. Авторизація відбувається в резолверах або через хук authorized? graphql-ruby на типах та мутаціях, викидаючи GraphQL::ExecutionError при відмові в доступі.
Більше про підготовку до співбесіди Ruby on Rails дивіться у модулі Rails API Mode, який охоплює патерни проєктування REST API, що доповнюють знання GraphQL.
Починай практикувати!
Перевір свої знання з нашими симуляторами співбесід та технічними тестами.
Створення продакшн GraphQL API з Rails: ключові висновки
- Встановлення graphql-ruby з
rails generate graphql:installстворює схему, типи та маршрутизацію - Використання джерел DataLoader для всіх асоціацій дозволяє пакетувати запити до бази даних та запобігати проблемам продуктивності N+1
- Мутації визначаються в окремих класах з явним полем помилок у типі повернення
- Інтеграція підписок через ActionCable забезпечує оновлення в реальному часі, що запускаються з callback'ів моделей або сервісних об'єктів
- Тестування запитів та мутацій здійснюється шляхом їх виконання безпосередньо до схеми в RSpec
- Автентифікація належить контролеру; перевірки авторизації відбуваються всередині резолверів з використанням
context[:current_user] - Обирайте GraphQL, коли клієнтам потрібне гнучке отримання даних; залишайтеся з REST для простих CRUD API з однорідними відповідями
Чи знайдеш ти помилку в Ruby on Rails?
Справжній фрагмент коду, прихована помилка, одна спроба на день. Щоб спробувати, акаунт не потрібен.

Автор:
Anthony Fillion-MailletЗасновник SharpSkill
Fullstack-розробник понад 10 років. Керує SharpSkill і відповідає за все, що тут публікується.
Оновлено 19 вересня 2026 р.
Поділитися
Пов'язані статті

Фонові Завдання Rails 2026: Sidekiq vs Good Job — Порівняння та Питання на Співбесідах
Комплексне порівняння Sidekiq, Good Job та Solid Queue у Rails 8. Аналіз продуктивності, функціональності та найпоширеніші питання на співбесідах щодо background jobs у Ruby on Rails.

Rails Active Storage у 2026: Завантаження Файлів, Інтеграція з S3 та Питання на Співбесідах
Повний посібник з Rails Active Storage у 2026 році. Налаштування S3, пряме завантаження, обробка зображень та найпоширеніші питання на технічних співбесідах.

Rails Stimulus та Importmaps у 2026: Сучасний JavaScript без інструментів збірки
Повний посібник зі Stimulus та Importmaps у Rails 8 - як створювати інтерактивні веб-застосунки без webpack, esbuild чи будь-яких бандлерів.