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.

API GraphQL con Rails en 2026: graphql-ruby, Subscriptions y Preguntas de Entrevista

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.

Lo que graphql-ruby aporta a Rails

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.

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

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

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

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

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

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

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

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

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

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

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

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

El context[:current_user] proviene del controlador. La autenticacion ocurre antes de la capa GraphQL, tipicamente usando Devise o una 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

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

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 # El objeto pasado desde trigger
    end
  end
end

Disparar una subscription ocurre desde cualquier lugar de la aplicacion, tipicamente en un callback de modelo o 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

El canal ActionCable que maneja las subscriptions GraphQL viene incluido con la gema.

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

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

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

Las pruebas de mutations verifican tanto los caminos de exito como de errores de validacion.

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 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:install para 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
Reto diario

¿Sabrías detectar el bug en Ruby on Rails?

Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador 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