API GraphQL con Rails en 2026: graphql-ruby, Subscriptions y Preguntas de Entrevista
Construir una API GraphQL lista para produccion con Rails 8 y graphql-ruby. Diseno de esquema, mutaciones, subscriptions con ActionCable y preparacion para entrevistas.

GraphQL se ha convertido en la opcion predeterminada para APIs que requieren obtencion de datos flexible, y graphql-ruby lleva esta capacidad a Rails con una integracion estrecha con ActiveRecord. Rails 8, lanzado a finales de 2024, se combina perfectamente con graphql-ruby 2.4, que introdujo un manejo mejorado de subscriptions y mejor ejecucion diferida para la prevencion de N+1.
La gema graphql-ruby proporciona una implementacion completa de GraphQL: definicion de esquema con un DSL de Ruby, inferencia automatica de tipos desde modelos ActiveRecord, DataLoader integrado para batching, e integracion con ActionCable para subscriptions en tiempo real.
Configuracion de graphql-ruby en una Aplicacion Rails 8
El generador de la gema crea la estructura inicial del esquema y monta el endpoint de GraphQL. Comienza con la gema y ejecuta el generador de instalacion para crear los archivos base.
# Gemfile
gem 'graphql', '~> 2.4'# Terminal
bundle install
rails generate graphql:installEl generador crea el directorio app/graphql/ con el archivo de esquema, tipos base y un directorio de mutations. Tambien agrega una ruta en /graphql y opcionalmente monta GraphiQL para desarrollo.
# app/graphql/sharpskill_schema.rb
class SharpskillSchema < GraphQL::Schema
mutation(Types::MutationType)
query(Types::QueryType)
subscription(Types::SubscriptionType)
# Usar el DataLoader integrado para prevenir N+1
use GraphQL::Dataloader
# Requerido para subscriptions
use GraphQL::Subscriptions::ActionCableSubscriptions
endEl archivo de esquema actua como el punto de entrada. Declara que tipos manejan queries, mutations y subscriptions, y registra cualquier middleware como DataLoader.
Definicion de Tipos y Resolvers para Modelos Rails
Cada modelo ActiveRecord que aparece en la API necesita un tipo GraphQL correspondiente. El tipo define que campos se exponen y como se resuelven.
# 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
# Asociacion con batching automatico via DataLoader
field :posts, [Types::PostType], null: false
def posts
dataloader.with(Sources::ActiveRecordCollection, Post, :user_id).load(object.id)
end
end
endLa llamada dataloader.with agrupa multiples obtenciones de posts en una sola consulta SQL. Sin DataLoader, una query solicitando 50 usuarios con sus posts ejecutaria 51 consultas. Con DataLoader, ejecuta 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
endEste patron aparece en todo proyecto graphql-ruby serio. La documentacion oficial de DataLoader cubre casos de uso adicionales como caching y sources anidados.
Construccion de Queries con Argumentos y Filtrado
El QueryType define los campos raiz disponibles para los clientes. Los argumentos permiten filtrado y paginacion.
# 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
endLos metodos resolver reciben argumentos con nombre que coinciden con la definicion del campo. Retornar nil desde un campo nullable es valido; lanzar un error desde un campo non-null dispara una respuesta de error GraphQL.
¿Listo para aprobar tus entrevistas de Ruby on Rails?
Practica con nuestros simuladores interactivos, flashcards y tests técnicos.
Mutations: Creacion y Actualizacion de Registros
Las mutations siguen una convencion mas estricta que las queries. Cada mutation vive en su propia clase y retorna un tipo payload que incluye tanto el resultado como cualquier error.
# 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
endEl context[:current_user] proviene del controlador. La autenticacion ocurre antes de la capa GraphQL, tipicamente usando Devise o una 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
endEl control de acceso dentro de los resolvers verifica context[:current_user] y lanza GraphQL::ExecutionError cuando no esta autorizado. La guia de autorizacion de graphql-ruby detalla patrones de autorizacion a nivel de campo y tipo.
Actualizaciones en Tiempo Real con GraphQL Subscriptions y ActionCable
Las subscriptions permiten que los clientes reciban actualizaciones cuando ocurren eventos del lado del servidor. Rails ActionCable maneja la conexion WebSocket, y graphql-ruby se 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 # El objeto pasado desde trigger
end
end
endDisparar una subscription ocurre desde cualquier lugar de la aplicacion, tipicamente en un callback de modelo o 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
endEl canal ActionCable que maneja las subscriptions GraphQL viene incluido con la gema.
# 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
endEn el cliente, bibliotecas como Apollo Client o urql se conectan al WebSocket de ActionCable y gestionan el estado de las subscriptions. Para preparacion de entrevistas sobre mecanicas de ActionCable, consultar el modulo ActionCable & WebSockets.
Testing de APIs GraphQL con RSpec
Probar una API GraphQL requiere ejecutar queries contra el esquema y verificar la estructura y datos de la respuesta.
# 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
endLas pruebas de mutations verifican tanto los caminos de exito como de errores de validacion.
# 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 mas informacion sobre patrones de testing en Rails, consultar el modulo Testing con RSpec.
Preguntas de Entrevista GraphQL Comunes para Desarrolladores Rails
Los entrevistadores que evaluan conocimientos de GraphQL en un contexto Rails frecuentemente se enfocan en preocupaciones practicas de implementacion en lugar de teoria abstracta de tipos.
Como se previenen las consultas N+1 en graphql-ruby?
Usar DataLoader con sources personalizados. Definir una clase source que agrupa llamadas a la base de datos, y llamar dataloader.with(SourceClass, args).load(id) en los resolvers. DataLoader recolecta todos los IDs solicitados en una sola ejecucion GraphQL y los obtiene en una consulta.
Cual es la diferencia entre una query y una mutation?
Semantica: las queries leen datos y no deben tener efectos secundarios. Las mutations modifican datos y pueden tener efectos secundarios. GraphQL garantiza ejecucion paralela para campos de query y ejecucion secuencial para campos de mutation, por lo que dos mutations en la misma solicitud se ejecutan en orden.
Como funcionan las subscriptions en Rails?
Las subscriptions usan ActionCable para conexiones WebSocket. El cliente envia una query de subscription a traves del WebSocket. El servidor almacena la subscription y, cuando se llama a trigger, envia el resultado a todos los clientes que coincidan. El ID de subscription permite a los clientes cancelar la suscripcion.
Cuando elegir GraphQL sobre REST para una API Rails?
GraphQL reduce la sobre-obtencion cuando los clientes necesitan diferentes subconjuntos de datos, lo cual es comun en aplicaciones moviles con tamanos de pantalla variables. Tambien elimina la necesidad de multiples endpoints REST cuando una sola vista requiere datos de varios modelos. REST sigue siendo mas simple para APIs CRUD con formas de datos consistentes y para APIs publicas donde el caching es critico.
Como se maneja la autenticacion y autorizacion en graphql-ruby?
La autenticacion ocurre en el controlador antes de la ejecucion GraphQL, tipicamente via Devise, Warden o verificacion JWT. El usuario autenticado se pasa en context. La autorizacion ocurre en los resolvers o via el hook authorized? de graphql-ruby en tipos y mutations, lanzando GraphQL::ExecutionError cuando se deniega el acceso.
Para mas preparacion de entrevistas Ruby on Rails, consultar el modulo Rails API Mode, que cubre patrones de diseno de API REST que complementan el conocimiento de GraphQL.
¡Empieza a practicar!
Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.
Construccion de APIs GraphQL de Produccion con Rails: Puntos Clave
- Instalar graphql-ruby con
rails generate graphql:installpara generar el esquema, tipos y enrutamiento - Usar sources de DataLoader para todas las asociaciones para agrupar consultas de base de datos y prevenir problemas de rendimiento N+1
- Definir mutations en clases separadas con campos de error explicitos en el tipo de retorno
- Integrar subscriptions via ActionCable para actualizaciones en tiempo real, disparando desde callbacks de modelo o service objects
- Probar queries y mutations ejecutandolas directamente contra el esquema en RSpec
- La autenticacion pertenece al controlador; las verificaciones de autorizacion ocurren dentro de los resolvers usando
context[:current_user] - Elegir GraphQL cuando los clientes necesitan obtencion de datos flexible; quedarse con REST para APIs CRUD simples con respuestas uniformes
¿Sabrías detectar el bug en Ruby on Rails?
Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Escrito por
Anthony Fillion-MailletFundador de SharpSkill
Desarrollador fullstack desde hace más de 10 años. Dirige SharpSkill y responde por todo lo que se publica aquí.
Actualizado el 19 de septiembre de 2026
Compartir
Artículos relacionados

Jobs en segundo plano en Rails 2026: Sidekiq vs Good Job y preguntas de entrevista
Comparación completa entre Sidekiq, Good Job y Solid Queue para tareas asíncronas en Rails, con las preguntas técnicas más frecuentes en entrevistas.

Rails Active Storage en 2026: Subida de archivos, integración con S3 y preguntas de entrevista
Guía completa sobre Rails Active Storage en 2026: configuración de subida de archivos, integración con AWS S3, variantes de imágenes y preguntas técnicas para preparar entrevistas de Ruby on Rails.

Rails Stimulus e Importmaps en 2026: JavaScript Moderno Sin Herramientas de Build
Guía completa sobre Rails Stimulus e Importmaps en 2026. Aprende a crear aplicaciones JavaScript modernas sin webpack ni bundlers complejos usando Hotwire.