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.

API GraphQL com Rails em 2026: graphql-ruby, Subscriptions e Perguntas de Entrevista

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.

O que graphql-ruby traz para Rails

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.

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

O 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.

ruby
# 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
end

O 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.

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

    # 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
end

A 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.

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

Esse 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.

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

Os 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.

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

O context[:current_user] vem do controller. A autenticacao acontece antes da camada GraphQL, tipicamente usando Devise ou uma biblioteca JWT.

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

O 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.

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 # O objeto passado do trigger
    end
  end
end

Disparar uma subscription acontece de qualquer lugar na aplicacao, tipicamente em um callback de modelo ou 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

O canal ActionCable que trata as subscriptions GraphQL vem incluido com a gem.

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

No 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.

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

Os testes de mutations verificam tanto os caminhos de sucesso quanto de erros de validacao.

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

Para 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:install para 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
Desafio do dia

Você saberia encontrar o bug em Ruby on Rails?

Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador 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