GraphQL APIs with graphql-ruby gem

Sarah Mitchell Feb 2026
3 tabs
# Gemfile
gem 'graphql'

# Installation
# rails generate graphql:install

# app/graphql/types/user_type.rb
module Types
  class UserType < Types::BaseObject
    field :id, ID, null: false
    field :name, String, null: false
    field :email, String, null: false
    field :bio, String, null: true
    field :created_at, GraphQL::Types::ISO8601DateTime, null: false

    # Association
    field :posts, [Types::PostType], null: false

    # Computed field
    field :posts_count, Integer, null: false do
      description "Total number of posts by user"
    end

    def posts_count
      object.posts.count
    end

    # Field with arguments
    field :recent_posts, [Types::PostType], null: false do
      argument :limit, Integer, required: false, default_value: 10
    end

    def recent_posts(limit:)
      object.posts.order(created_at: :desc).limit(limit)
    end

    # Authorized field
    field :private_data, String, null: true do
      description "Visible only to user themselves"
    end

    def private_data
      return nil unless context[:current_user]&.id == object.id

      object.private_data
    end
  end
end

# app/graphql/types/post_type.rb
module Types
  class PostType < Types::BaseObject
    field :id, ID, null: false
    field :title, String, null: false
    field :content, String, null: false
    field :published, Boolean, null: false
    field :created_at, GraphQL::Types::ISO8601DateTime, null: false

    field :user, Types::UserType, null: false
    field :comments, [Types::CommentType], null: false

    # Custom resolver
    field :excerpt, String, null: false

    def excerpt
      object.content.truncate(100)
    end
  end
end

# app/graphql/types/query_type.rb
module Types
  class QueryType < Types::BaseObject
    # Fetch single user
    field :user, Types::UserType, null: true do
      description "Find a user by ID"
      argument :id, ID, required: true
    end

    def user(id:)
      User.find(id)
    end

    # Fetch all users with filtering
    field :users, [Types::UserType], null: false do
      argument :role, String, required: false
      argument :search, String, required: false
    end

    def users(role: nil, search: nil)
      users = User.all
      users = users.where(role: role) if role.present?
      users = users.where("name ILIKE ?", "%#{search}%") if search.present?
      users
    end

    # Current user
    field :current_user, Types::UserType, null: true

    def current_user
      context[:current_user]
    end

    # Posts with pagination
    field :posts, Types::PostType.connection_type, null: false do
      argument :published_only, Boolean, required: false, default_value: false
    end

    def posts(published_only:)
      posts = Post.all
      posts = posts.where(published: true) if published_only
      posts
    end
  end
end
3 files · ruby Explain with highlit

GraphQL enables clients to request exactly the data they need. The graphql-ruby gem implements GraphQL servers in Rails. Types define data structures—ObjectTypes for models, InputTypes for mutations. Queries fetch data; mutations modify data. Resolvers contain business logic for field resolution. I use GraphQL for flexible APIs—mobile apps request different fields than web. DataLoader batches and caches database queries, preventing N+1s. GraphQL introspection enables GraphiQL playground for testing. Subscriptions push real-time updates via ActionCable. Understanding GraphQL's query language and type system is essential. GraphQL reduces API versioning needs—add fields without breaking clients. Testing uses GraphQL execution with mocked contexts.