Rails GraphQL API trong 2026: graphql-ruby, Subscriptions va Cau hoi Phong van

Xay dung GraphQL API san sang cho production voi Rails 8 va graphql-ruby. Thiet ke schema, mutations, subscriptions voi ActionCable, va chuan bi phong van.

Rails GraphQL API trong 2026: graphql-ruby, Subscriptions va Cau hoi Phong van

GraphQL da tro thanh lua chon mac dinh cho cac API can truy xuat du lieu linh hoat, va graphql-ruby mang kha nang nay den Rails voi tich hop ActiveRecord chat che. Rails 8, phat hanh cuoi nam 2024, ket hop tot voi graphql-ruby 2.4, phien ban da gioi thieu xu ly subscription duoc cai thien va lazy execution tot hon de ngan chan N+1.

graphql-ruby mang den gi cho Rails

Gem graphql-ruby cung cap mot trien khai GraphQL day du: dinh nghia schema voi DSL Ruby, suy luan kieu tu dong tu cac model ActiveRecord, DataLoader tich hop san de batching, va tich hop ActionCable cho subscriptions thoi gian thuc.

Thiet lap graphql-ruby trong Ung dung Rails 8

Generator cua gem tao cau truc schema ban dau va mount endpoint GraphQL. Bat dau voi gem va chay generator install de tao scaffold cac file co ban.

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

Generator tao thu muc app/graphql/ voi file schema, base types, va thu muc mutations. No cung them route tai /graphql va tuy chon mount GraphiQL cho moi truong phat trien.

ruby
# app/graphql/sharpskill_schema.rb
class SharpskillSchema < GraphQL::Schema
  mutation(Types::MutationType)
  query(Types::QueryType)
  subscription(Types::SubscriptionType)

  # Su dung DataLoader tich hop san de ngan chan N+1
  use GraphQL::Dataloader

  # Bat buoc cho subscriptions
  use GraphQL::Subscriptions::ActionCableSubscriptions
end

File schema dong vai tro la diem vao. No khai bao cac type nao xu ly queries, mutations, va subscriptions, dong thoi dang ky bat ky middleware nao nhu DataLoader.

Dinh nghia Types va Resolvers cho cac Model Rails

Moi model ActiveRecord xuat hien trong API can mot type GraphQL tuong ung. Type dinh nghia cac field nao duoc expose va cach chung duoc resolve.

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

    # Association voi batching tu dong qua DataLoader
    field :posts, [Types::PostType], null: false

    def posts
      dataloader.with(Sources::ActiveRecordCollection, Post, :user_id).load(object.id)
    end
  end
end

Loi goi dataloader.with nhom nhieu lan lay posts vao mot query SQL duy nhat. Khong co DataLoader, mot query yeu cau 50 user cung voi posts cua ho se thuc thi 51 query. Voi DataLoader, chi chay 2 query.

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

Mau nay xuat hien trong moi du an graphql-ruby nghiem tuc. Tai lieu chinh thuc cua DataLoader bao gom cac truong hop su dung bo sung nhu caching va nested sources.

Xay dung Queries voi Arguments va Filtering

QueryType dinh nghia cac field goc co san cho client. Arguments cho phep filtering va pagination.

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

Cac phuong thuc resolver nhan keyword arguments khop voi dinh nghia field. Tra ve nil tu mot field nullable la hop le; nem loi tu mot field non-null se kich hoat phan hoi loi GraphQL.

Sẵn sàng chinh phục phỏng vấn Ruby on Rails?

Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.

Mutations: Tao va Cap nhat Records

Mutations tuan theo quy uoc chat che hon queries. Moi mutation nam trong class rieng va tra ve mot payload type bao gom ca ket qua va bat ky loi nao.

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

context[:current_user] den tu controller. Xac thuc xay ra truoc lop GraphQL, thuong su dung Devise hoac thu vien 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

Kiem soat truy cap ben trong resolvers kiem tra context[:current_user] va nem GraphQL::ExecutionError khi khong duoc phep. Huong dan uy quyen cua graphql-ruby trinh bay chi tiet cac mau uy quyen cap field va cap type.

Cap nhat Thoi gian thuc voi GraphQL Subscriptions va ActionCable

Subscriptions cho phep client nhan cap nhat khi cac su kien phia server xay ra. Rails ActionCable xu ly ket noi WebSocket, va graphql-ruby tich hop thong qua 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 # Object duoc truyen tu trigger
    end
  end
end

Kich hoat subscription xay ra tu bat ky dau trong ung dung, thuong trong model callback hoac 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

ActionCable channel xu ly subscriptions GraphQL duoc cung cap cung voi 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

Phia client, cac thu vien nhu Apollo Client hoac urql ket noi voi WebSocket ActionCable va quan ly trang thai subscription. De chuan bi phong van ve co che ActionCable, xem module ActionCable & WebSockets.

Testing GraphQL API voi RSpec

Testing mot GraphQL API yeu cau thuc thi queries tren schema va assertion ve cau truc va du lieu phan hoi.

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

Cac test mutation xac minh ca duong dan thanh cong va loi validation.

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

De biet them ve cac mau testing Rails, xem module Testing voi RSpec.

Cac Cau hoi Phong van GraphQL Pho bien cho Rails Developer

Nguoi phong van kiem tra kien thuc GraphQL trong boi canh Rails thuong tap trung vao cac van de trien khai thuc te hon la ly thuyet kieu truu tuong.

Lam the nao de ngan chan query N+1 trong graphql-ruby?

Su dung DataLoader voi custom sources. Dinh nghia mot class source nhom cac cuoc goi database, va goi dataloader.with(SourceClass, args).load(id) trong resolvers. DataLoader thu thap tat ca ID duoc yeu cau trong mot lan thuc thi GraphQL va lay chung trong mot query.

Su khac biet giua query va mutation la gi?

Ngu nghia: queries doc du lieu va khong nen co tac dung phu. Mutations thay doi du lieu va co the co tac dung phu. GraphQL dam bao thuc thi song song cho cac field query va thuc thi tuan tu cho cac field mutation, nen hai mutation trong cung mot request se thuc thi theo thu tu.

Subscriptions hoat dong nhu the nao trong Rails?

Subscriptions su dung ActionCable cho ket noi WebSocket. Client gui query subscription qua WebSocket. Server luu subscription va, khi trigger duoc goi, day ket qua den tat ca client khop. ID subscription cho phep client unsubscribe.

Khi nao nen chon GraphQL thay vi REST cho Rails API?

GraphQL giam over-fetching khi client can cac tap hop du lieu khac nhau, dieu nay pho bien trong ung dung mobile voi cac kich thuoc man hinh khac nhau. No cung loai bo nhu cau co nhieu REST endpoints khi mot view yeu cau du lieu tu nhieu model. REST van don gian hon cho cac API CRUD nang voi hinh dang du lieu nhat quan va cho cac API cong khai noi caching la quan trong.

Lam the nao de xu ly xac thuc va uy quyen trong graphql-ruby?

Xac thuc xay ra trong controller truoc khi thuc thi GraphQL, thuong thong qua Devise, Warden, hoac xac minh JWT. Nguoi dung da xac thuc duoc truyen trong context. Uy quyen xay ra trong resolvers hoac thong qua hook authorized? cua graphql-ruby tren types va mutations, nem GraphQL::ExecutionError khi bi tu choi truy cap.

De biet them ve chuan bi phong van Ruby on Rails, xem module Rails API Mode, bao gom cac mau thiet ke REST API bo sung kien thuc GraphQL.

Bắt đầu luyện tập!

Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.

Xay dung GraphQL API Production voi Rails: Nhung Diem Chinh

  • Cai dat graphql-ruby voi rails generate graphql:install de tao scaffold schema, types, va routing
  • Su dung DataLoader sources cho tat ca associations de nhom cac query database va ngan chan van de hieu suat N+1
  • Dinh nghia mutations trong cac class rieng biet voi cac field error ro rang trong kieu tra ve
  • Tich hop subscriptions qua ActionCable cho cap nhat thoi gian thuc, kich hoat tu model callbacks hoac service objects
  • Test queries va mutations bang cach thuc thi chung truc tiep tren schema trong RSpec
  • Xac thuc nam o controller; kiem tra uy quyen xay ra ben trong resolvers su dung context[:current_user]
  • Chon GraphQL khi client can truy xuat du lieu linh hoat; giu REST cho cac API CRUD don gian voi phan hoi dong nhat
Thử thách hôm nay

Bạn có tìm ra lỗi trong Ruby on Rails không?

Một đoạn mã thật, một lỗi ẩn, mỗi ngày một lượt. Không cần tài khoản để thử.

Anthony Fillion-Maillet

Viết bởi

Anthony Fillion-Maillet

Người sáng lập SharpSkill

Lập trình viên fullstack hơn 10 năm. Anh điều hành SharpSkill và chịu trách nhiệm về mọi nội dung đăng tại đây.

Cập nhật ngày 19 tháng 9, 2026

Chia sẻ

Bài viết liên quan