Core classes and methods for defining GraphQL schemas, including schema setup, object types, fields, arguments, and type relationships in GraphQL Ruby.
The root schema class that ties together all GraphQL types and provides the execution interface.
class GraphQL::Schema
# Configure root types
def self.query(query_type)
def self.mutation(mutation_type)
def self.subscription(subscription_type)
# Schema configuration
def self.max_depth(value)
def self.max_complexity(value)
def self.default_max_page_size(value)
def self.orphan_types(*types)
def self.use(plugin, **options)
# Execution interface
def self.execute(query_string, variables: {}, context: {}, operation_name: nil, only: nil, except: nil)
def self.multiplex(queries, context: {}, max_complexity: nil)
# Introspection and utilities
def self.get_type(type_name)
def self.types(context = GraphQL::Query::NullContext.instance)
def self.to_definition
def self.to_json(**args)
endUsage Examples:
class MySchema < GraphQL::Schema
query QueryType
mutation MutationType
subscription SubscriptionType
# Configuration
max_depth 15
max_complexity 1000
use GraphQL::Dataloader
use GraphQL::Subscriptions::ActionCableSubscriptions
# Custom error handling
def self.type_error(err, context)
# Handle type resolution errors
GraphQL::ExecutionError.new("Type error: #{err.message}")
end
# Authorization callback
def self.unauthorized_object(err)
raise GraphQL::ExecutionError, "Unauthorized access"
end
end
# Execute queries
result = MySchema.execute(
'query GetUser($id: ID!) { user(id: $id) { name email } }',
variables: { id: "123" },
context: { current_user: current_user }
)Define GraphQL object types that represent data structures in your API.
class GraphQL::Schema::Object
# Type metadata
def self.description(text)
def self.graphql_name(name)
# Field definitions
def self.field(name, type, null:, description: nil, deprecation_reason: nil, &block)
def self.fields
# Interface implementation
def self.implements(*interfaces)
def self.interfaces
# Authorization
def self.authorized?(object, context)
def self.scope_items(items, context)
# Introspection helpers
def self.visible?(context)
def self.accessible?(context)
endUsage Examples:
class UserType < GraphQL::Schema::Object
description "A user in the system"
field :id, ID, null: false, description: "Unique identifier"
field :name, String, null: true, description: "Display name"
field :email, String, null: false, description: "Email address"
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :posts, [PostType], null: true, description: "User's posts"
# Custom field with arguments
field :posts_by_status, [PostType], null: true do
argument :status, PostStatusEnum, required: true
argument :limit, Integer, required: false, default_value: 10
end
# Resolver methods
def posts_by_status(status:, limit: 10)
object.posts.where(status: status).limit(limit)
end
# Authorization
def self.authorized?(user, context)
context[:current_user]&.can_view?(user)
end
# Custom field authorization
field :private_notes, String, null: true do
def authorized?(object, args, context)
context[:current_user] == object
end
end
endIndividual field specifications with types, arguments, and resolver logic.
class GraphQL::Schema::Field
def initialize(name, type, null:, description: nil, deprecation_reason: nil)
# Arguments
def argument(name, type, required: false, default_value: nil, description: nil)
def arguments
# Metadata
def description(text = nil)
def deprecation_reason(reason = nil)
# Authorization and visibility
def authorized?(object, args, context)
def visible?(context)
# Execution hooks
def resolve(object, args, context)
endUsage Examples:
# Field with complex arguments
field :search_posts, [PostType], null: false do
description "Search posts with filters and pagination"
argument :query, String, required: false, description: "Text search query"
argument :category_ids, [ID], required: false, description: "Filter by categories"
argument :published_after, GraphQL::Types::ISO8601DateTime, required: false
argument :limit, Integer, required: false, default_value: 20
argument :offset, Integer, required: false, default_value: 0
end
def search_posts(query: nil, category_ids: nil, published_after: nil, limit: 20, offset: 0)
scope = Post.published
scope = scope.where("title ILIKE ?", "%#{query}%") if query
scope = scope.where(category_id: category_ids) if category_ids&.any?
scope = scope.where("published_at > ?", published_after) if published_after
scope.limit(limit).offset(offset)
end
# Field with authorization
field :admin_notes, String, null: true do
description "Internal admin notes (admin only)"
def authorized?(object, args, context)
context[:current_user]&.admin?
end
endDefine and validate arguments for GraphQL fields and mutations.
class GraphQL::Schema::Argument
def initialize(name, type, description: nil, required: false, default_value: nil)
# Properties
def type
def required?
def optional?
def default_value
def description
# Validation and preparation
def prepare_value(object, value, context)
def authorized?(object, value, context)
endUsage Examples:
# Custom argument preparation
class UserSearchType < GraphQL::Schema::InputObject
argument :name, String, required: false do
description "Name to search for"
def prepare_value(object, value, context)
# Normalize the search value
value&.strip&.downcase
end
end
argument :email_domain, String, required: false do
def authorized?(object, value, context)
# Only admins can search by email domain
context[:current_user]&.admin?
end
end
end
# Field using custom input
field :search_users, [UserType], null: false do
argument :filters, UserSearchType, required: false
argument :limit, Integer, required: false, default_value: 50
end
def search_users(filters: nil, limit: 50)
scope = User.all
if filters
scope = scope.where("LOWER(name) LIKE ?", "%#{filters[:name]}%") if filters[:name]
scope = scope.where("email LIKE ?", "%@#{filters[:email_domain]}") if filters[:email_domain]
end
scope.limit(limit)
endDefine input types for mutations and complex field arguments.
class GraphQL::Schema::InputObject
# Argument definitions
def self.argument(name, type, required: false, default_value: nil, description: nil)
def self.arguments
# Metadata
def self.description(text)
def self.graphql_name(name)
# Conversion helpers
def to_h
def to_hash
def [] (key)
# Validation hooks
def self.coerce_input(value, context)
def self.coerce_result(value, context)
endUsage Examples:
class CreateUserInput < GraphQL::Schema::InputObject
description "Input for creating a new user"
argument :name, String, required: true, description: "User's display name"
argument :email, String, required: true, description: "User's email address"
argument :password, String, required: true, description: "User's password"
argument :role, UserRoleEnum, required: false, default_value: "USER"
argument :metadata, GraphQL::Types::JSON, required: false
# Custom validation
def self.coerce_input(value, context)
input = super
# Validate email format
unless input[:email] =~ URI::MailTo::EMAIL_REGEXP
raise GraphQL::CoercionError, "Invalid email format"
end
# Validate password strength
if input[:password].length < 8
raise GraphQL::CoercionError, "Password must be at least 8 characters"
end
input
end
end
# Usage in mutation
class CreateUser < GraphQL::Schema::Mutation
argument :input, CreateUserInput, required: true
field :user, UserType, null: true
field :errors, [String], null: false
def resolve(input:)
user = User.create(input.to_h)
if user.persisted?
{ user: user, errors: [] }
else
{ user: nil, errors: user.errors.full_messages }
end
end
endDefine shared fields across multiple object types.
class GraphQL::Schema::Interface
include GraphQL::Schema::Interface
# Field definitions (same as Object)
def self.field(name, type, null:, description: nil)
def self.fields
# Type resolution
def self.resolve_type(object, context)
# Implementation checking
def self.coerce_input(value, context)
def self.coerce_result(value, context)
endUsage Examples:
module NodeInterface
include GraphQL::Schema::Interface
description "An object with a global ID"
field :id, ID, null: false, description: "Global object identifier"
def self.resolve_type(object, context)
case object
when User then UserType
when Post then PostType
when Comment then CommentType
else
raise GraphQL::RequiredImplementationMissingError, "Unknown type: #{object.class}"
end
end
end
# Implementing the interface
class UserType < GraphQL::Schema::Object
implements NodeInterface
# id field is inherited from NodeInterface
field :name, String, null: true
field :email, String, null: false
end
class PostType < GraphQL::Schema::Object
implements NodeInterface
field :title, String, null: false
field :content, String, null: true
field :author, UserType, null: false
end