API GraphQL com Rails em 2026: graphql-ruby, Subscriptions e Perguntas de Entrevista
Construir uma API GraphQL pronta para producao com Rails 8 e graphql-ruby. Design de schema, mutations, subscriptions com ActionCable e preparacao para entrevistas.

GraphQL se tornou a escolha padrao para APIs que precisam de obtencao de dados flexivel, e graphql-ruby traz essa capacidade para Rails com integracao estreita com ActiveRecord. Rails 8, lancado no final de 2024, funciona perfeitamente com graphql-ruby 2.4, que introduziu tratamento melhorado de subscriptions e melhor execucao lazy para prevencao de N+1.
A gem graphql-ruby fornece uma implementacao completa de GraphQL: definicao de schema com um DSL Ruby, inferencia automatica de tipos a partir de modelos ActiveRecord, DataLoader integrado para batching, e integracao com ActionCable para subscriptions em tempo real.
Configurando graphql-ruby em uma Aplicacao Rails 8
O gerador da gem cria a estrutura inicial do schema e monta o endpoint GraphQL. Comece com a gem e execute o gerador de instalacao para criar os arquivos base.
# Gemfile
gem 'graphql', '~> 2.4'# Terminal
bundle install
rails generate graphql:installO gerador cria o diretorio app/graphql/ com o arquivo de schema, tipos base e um diretorio de mutations. Ele tambem adiciona uma rota em /graphql e opcionalmente monta o GraphiQL para desenvolvimento.
# app/graphql/sharpskill_schema.rb
class SharpskillSchema < GraphQL::Schema
mutation(Types::MutationType)
query(Types::QueryType)
subscription(Types::SubscriptionType)
# Usar o DataLoader integrado para prevenir N+1
use GraphQL::Dataloader
# Necessario para subscriptions
use GraphQL::Subscriptions::ActionCableSubscriptions
endO arquivo de schema atua como o ponto de entrada. Ele declara quais tipos tratam queries, mutations e subscriptions, e registra qualquer middleware como DataLoader.
Definindo Tipos e Resolvers para Modelos Rails
Cada modelo ActiveRecord que aparece na API precisa de um tipo GraphQL correspondente. O tipo define quais campos sao expostos e como eles sao resolvidos.
# 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
# Associacao com batching automatico via DataLoader
field :posts, [Types::PostType], null: false
def posts
dataloader.with(Sources::ActiveRecordCollection, Post, :user_id).load(object.id)
end
end
endA chamada dataloader.with agrupa multiplas obtencoes de posts em uma unica consulta SQL. Sem DataLoader, uma query solicitando 50 usuarios com seus posts executaria 51 consultas. Com DataLoader, executa apenas 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
endEsse padrao aparece em todo projeto graphql-ruby serio. A documentacao oficial do DataLoader cobre casos de uso adicionais como caching e sources aninhados.
Construindo Queries com Argumentos e Filtragem
O QueryType define os campos raiz disponiveis para os clientes. Os argumentos permitem filtragem e paginacao.
# 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
endOs metodos resolver recebem argumentos nomeados correspondentes a definicao do campo. Retornar nil de um campo nullable e valido; lancar um erro de um campo non-null dispara uma resposta de erro GraphQL.
Pronto para mandar bem nas entrevistas de Ruby on Rails?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Mutations: Criando e Atualizando Registros
As mutations seguem uma convencao mais rigorosa que as queries. Cada mutation vive em sua propria classe e retorna um tipo payload que inclui tanto o resultado quanto quaisquer erros.
# 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
endO context[:current_user] vem do controller. A autenticacao acontece antes da camada GraphQL, tipicamente usando Devise ou uma biblioteca 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
endO controle de acesso dentro dos resolvers verifica context[:current_user] e lanca GraphQL::ExecutionError quando nao autorizado. O guia de autorizacao do graphql-ruby detalha padroes de autorizacao em nivel de campo e tipo.
Atualizacoes em Tempo Real com GraphQL Subscriptions e ActionCable
As subscriptions permitem que os clientes recebam atualizacoes quando eventos do lado do servidor ocorrem. Rails ActionCable trata a conexao WebSocket, e graphql-ruby integra via 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 # O objeto passado do trigger
end
end
endDisparar uma subscription acontece de qualquer lugar na aplicacao, tipicamente em um callback de modelo ou service object.
# 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
endO canal ActionCable que trata as subscriptions GraphQL vem incluido com a 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
endNo cliente, bibliotecas como Apollo Client ou urql conectam ao WebSocket do ActionCable e gerenciam o estado das subscriptions. Para preparacao de entrevistas sobre mecanicas do ActionCable, consulte o modulo ActionCable & WebSockets.
Testando APIs GraphQL com RSpec
Testar uma API GraphQL requer executar queries contra o schema e verificar a estrutura e dados da resposta.
# 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
endOs testes de mutations verificam tanto os caminhos de sucesso quanto de erros de validacao.
# 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
endPara mais informacoes sobre padroes de teste em Rails, consulte o modulo Testing com RSpec.
Perguntas de Entrevista GraphQL Comuns para Desenvolvedores Rails
Os entrevistadores que testam conhecimento de GraphQL em um contexto Rails frequentemente focam em preocupacoes praticas de implementacao em vez de teoria abstrata de tipos.
Como prevenir consultas N+1 em graphql-ruby?
Usar DataLoader com sources personalizados. Definir uma classe source que agrupa chamadas ao banco de dados, e chamar dataloader.with(SourceClass, args).load(id) nos resolvers. DataLoader coleta todos os IDs solicitados em uma unica execucao GraphQL e os obtem em uma consulta.
Qual e a diferenca entre uma query e uma mutation?
Semantica: queries leem dados e nao devem ter efeitos colaterais. Mutations modificam dados e podem ter efeitos colaterais. GraphQL garante execucao paralela para campos de query e execucao sequencial para campos de mutation, entao duas mutations na mesma requisicao executam em ordem.
Como funcionam as subscriptions em Rails?
As subscriptions usam ActionCable para conexoes WebSocket. O cliente envia uma query de subscription atraves do WebSocket. O servidor armazena a subscription e, quando trigger e chamado, envia o resultado para todos os clientes correspondentes. O ID da subscription permite que os clientes cancelem a inscricao.
Quando escolher GraphQL em vez de REST para uma API Rails?
GraphQL reduz a sobre-obtencao quando os clientes precisam de diferentes subconjuntos de dados, o que e comum em aplicacoes moveis com tamanhos de tela variaveis. Ele tambem elimina a necessidade de multiplos endpoints REST quando uma unica view requer dados de varios modelos. REST permanece mais simples para APIs CRUD com formas de dados consistentes e para APIs publicas onde o caching e critico.
Como lidar com autenticacao e autorizacao em graphql-ruby?
A autenticacao acontece no controller antes da execucao GraphQL, tipicamente via Devise, Warden ou verificacao JWT. O usuario autenticado e passado no context. A autorizacao acontece nos resolvers ou via o hook authorized? do graphql-ruby em tipos e mutations, lancando GraphQL::ExecutionError quando o acesso e negado.
Para mais preparacao para entrevistas Ruby on Rails, consulte o modulo Rails API Mode, que cobre padroes de design de API REST que complementam o conhecimento de GraphQL.
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
Construindo APIs GraphQL de Producao com Rails: Pontos Principais
- Instalar graphql-ruby com
rails generate graphql:installpara gerar o schema, tipos e roteamento - Usar sources do DataLoader para todas as associacoes para agrupar consultas ao banco de dados e prevenir problemas de performance N+1
- Definir mutations em classes separadas com campos de erro explicitos no tipo de retorno
- Integrar subscriptions via ActionCable para atualizacoes em tempo real, disparando de callbacks de modelo ou service objects
- Testar queries e mutations executando-as diretamente contra o schema no RSpec
- A autenticacao pertence ao controller; verificacoes de autorizacao acontecem dentro dos resolvers usando
context[:current_user] - Escolher GraphQL quando os clientes precisam de obtencao de dados flexivel; ficar com REST para APIs CRUD simples com respostas uniformes
Você saberia encontrar o bug em Ruby on Rails?
Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Escrito por
Anthony Fillion-MailletFundador da SharpSkill
Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.
Atualizado em 19 de setembro de 2026
Compartilhar
Artigos relacionados

Jobs em segundo plano no Rails em 2026: Sidekiq vs Good Job e perguntas de entrevista
Comparação completa entre Sidekiq, Good Job e Solid Queue para tarefas assíncronas no Rails, com as perguntas técnicas mais frequentes em entrevistas.

Rails Active Storage em 2026: Upload de arquivos, integração com S3 e perguntas de entrevista
Guia completo sobre Rails Active Storage em 2026: configuração de upload de arquivos, integração com AWS S3, variantes de imagens e perguntas técnicas para preparar entrevistas de Ruby on Rails.

Rails Stimulus e Importmaps em 2026: JavaScript Moderno Sem Ferramentas de Build
Guia completo sobre Rails Stimulus e Importmaps em 2026. Aprenda a criar aplicações JavaScript modernas sem webpack ou bundlers complexos usando Hotwire.