Built-in scalar types, custom scalars, enums, interfaces, unions, and input objects that form GraphQL Ruby's type system.
GraphQL Ruby provides standard GraphQL scalar types and additional Ruby-specific types.
# Standard GraphQL scalars
GraphQL::Types::String # UTF-8 strings
GraphQL::Types::Int # 32-bit signed integers
GraphQL::Types::Float # Double-precision floating point
GraphQL::Types::Boolean # True or false values
GraphQL::Types::ID # Unique identifiers (serialized as strings)
# Extended Ruby scalars
GraphQL::Types::BigInt # Large integers beyond 32-bit limits
GraphQL::Types::JSON # JSON objects and arrays
# Date/Time types
GraphQL::Types::ISO8601Date # Date strings in ISO 8601 format
GraphQL::Types::ISO8601DateTime # DateTime strings in ISO 8601 format
GraphQL::Types::ISO8601Duration # Duration strings in ISO 8601 formatUsage Examples:
class UserType < GraphQL::Schema::Object
field :id, ID, null: false # "123" or "user_123"
field :name, String, null: true # "Alice Smith"
field :age, Int, null: true # 25
field :height, Float, null: true # 5.75
field :active, Boolean, null: false # true/false
field :follower_count, GraphQL::Types::BigInt # 2147483648
field :metadata, GraphQL::Types::JSON # {"theme": "dark"}
field :created_at, GraphQL::Types::ISO8601DateTime # "2023-01-15T10:30:00Z"
field :birth_date, GraphQL::Types::ISO8601Date # "1990-05-15"
end
# Using in queries
query = '
query {
user(id: "123") {
name # String
age # Int
active # Boolean
followerCount # BigInt
metadata # JSON
createdAt # ISO8601DateTime
}
}
'Define custom scalar types with validation and serialization logic.
class GraphQL::Schema::Scalar
# Input coercion (from query variables/literals)
def self.coerce_input(input_value, context)
# Output serialization (to JSON response)
def self.coerce_result(ruby_value, context)
# Metadata
def self.description(text = nil)
def self.graphql_name(name = nil)
# Validation helpers
def self.validate_input(input_value, context)
endUsage Examples:
# Email scalar with validation
class EmailType < GraphQL::Schema::Scalar
description "A valid email address"
def self.coerce_input(input_value, context)
if input_value.is_a?(String) && input_value =~ URI::MailTo::EMAIL_REGEXP
input_value.downcase
else
raise GraphQL::CoercionError, "#{input_value.inspect} is not a valid email"
end
end
def self.coerce_result(ruby_value, context)
ruby_value.to_s
end
end
# URL scalar
class URLType < GraphQL::Schema::Scalar
description "A valid HTTP or HTTPS URL"
def self.coerce_input(input_value, context)
uri = URI.parse(input_value.to_s)
if uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
uri.to_s
else
raise GraphQL::CoercionError, "#{input_value.inspect} is not a valid URL"
end
rescue URI::InvalidURIError
raise GraphQL::CoercionError, "#{input_value.inspect} is not a valid URL"
end
def self.coerce_result(ruby_value, context)
ruby_value.to_s
end
end
# Money scalar with currency
class MoneyType < GraphQL::Schema::Scalar
description "Monetary amount with currency (format: 'USD:1000' for $10.00)"
def self.coerce_input(input_value, context)
currency, cents = input_value.to_s.split(':')
{
currency: currency.upcase,
cents: cents.to_i
}
rescue
raise GraphQL::CoercionError, "#{input_value.inspect} is not valid money format"
end
def self.coerce_result(ruby_value, context)
if ruby_value.respond_to?(:currency) && ruby_value.respond_to?(:cents)
"#{ruby_value.currency}:#{ruby_value.cents}"
else
ruby_value.to_s
end
end
end
# Using custom scalars
class UserType < GraphQL::Schema::Object
field :email, EmailType, null: false
field :website, URLType, null: true
field :salary, MoneyType, null: true
endDefine enumeration types with specific allowed values.
class GraphQL::Schema::Enum
# Value definitions
def self.value(graphql_name, description = nil, value: graphql_name, deprecation_reason: nil)
def self.values
# Metadata
def self.description(text = nil)
def self.graphql_name(name = nil)
# Conversion
def self.coerce_input(value, context)
def self.coerce_result(value, context)
endUsage Examples:
# User status enum
class UserStatusEnum < GraphQL::Schema::Enum
description "User account status"
value "ACTIVE", "User is active and can log in"
value "SUSPENDED", "User is temporarily suspended"
value "DELETED", "User account has been deleted"
value "PENDING", "User account is pending verification"
end
# Post status with custom Ruby values
class PostStatusEnum < GraphQL::Schema::Enum
description "Publication status of a post"
value "DRAFT", "Post is being written", value: :draft
value "PUBLISHED", "Post is live", value: :published
value "ARCHIVED", "Post is archived", value: :archived
value "SCHEDULED", "Post is scheduled for future publication", value: :scheduled
end
# Priority enum with deprecation
class PriorityEnum < GraphQL::Schema::Enum
value "LOW", value: 1
value "MEDIUM", value: 2
value "HIGH", value: 3
value "URGENT", value: 4
value "CRITICAL", value: 5, deprecation_reason: "Use URGENT instead"
end
# Using enums in fields
class UserType < GraphQL::Schema::Object
field :status, UserStatusEnum, null: false
field :priority, PriorityEnum, null: true
end
class PostType < GraphQL::Schema::Object
field :status, PostStatusEnum, null: false
field :published_status, PostStatusEnum, null: false, method: :status
end
# Using enums in arguments
field :users_by_status, [UserType], null: false do
argument :status, UserStatusEnum, required: true
argument :priorities, [PriorityEnum], required: false
end
def users_by_status(status:, priorities: nil)
scope = User.where(status: status)
scope = scope.where(priority: priorities) if priorities&.any?
scope
endDefine union types that can represent one of several possible object types.
class GraphQL::Schema::Union
# Type definitions
def self.possible_types(*types)
def self.possible_types
# Type resolution
def self.resolve_type(object, context)
# Metadata
def self.description(text = nil)
def self.graphql_name(name = nil)Usage Examples:
# Search result union
class SearchResultUnion < GraphQL::Schema::Union
description "Items that can appear in search results"
possible_types UserType, PostType, CommentType, TagType
def self.resolve_type(object, context)
case object
when User then UserType
when Post then PostType
when Comment then CommentType
when Tag then TagType
else
raise GraphQL::RequiredImplementationMissingError, "Unexpected SearchResult type: #{object.class}"
end
end
end
# Content union for polymorphic associations
class ContentUnion < GraphQL::Schema::Union
description "Different types of content"
possible_types ArticleType, VideoType, ImageType, PodcastType
def self.resolve_type(object, context)
"#{object.class.name}Type".constantize
end
end
# Using unions in fields
class QueryType < GraphQL::Schema::Object
field :search, [SearchResultUnion], null: false do
argument :query, String, required: true
argument :limit, Int, required: false, default_value: 20
end
def search(query:, limit: 20)
results = []
results += User.where("name ILIKE ?", "%#{query}%").limit(limit / 4)
results += Post.where("title ILIKE ?", "%#{query}%").limit(limit / 4)
results += Comment.where("body ILIKE ?", "%#{query}%").limit(limit / 4)
results += Tag.where("name ILIKE ?", "%#{query}%").limit(limit / 4)
results.shuffle.first(limit)
end
end
# Using unions with fragments
query = '
query Search($query: String!) {
search(query: $query) {
... on User {
id
name
email
}
... on Post {
id
title
excerpt
}
... on Comment {
id
body
author { name }
}
... on Tag {
id
name
count
}
}
}
'Define shared fields that multiple object types can implement.
module GraphQL::Schema::Interface
include GraphQL::Schema::Interface
# Field definitions (same as Object types)
def self.field(name, type, null:, description: nil)
def self.fields
# Type resolution
def self.resolve_type(object, context)
# Implementation validation
def self.orphan_types(*types)
# Metadata
def self.description(text = nil)
def self.graphql_name(name = nil)
endUsage Examples:
# Node interface for Relay
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 Node type: #{object.class}"
end
end
end
# Timestamped interface
module TimestampedInterface
include GraphQL::Schema::Interface
description "An object with creation and update timestamps"
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
end
# Commentable interface
module CommentableInterface
include GraphQL::Schema::Interface
description "An object that can receive comments"
field :comments, [CommentType], null: false do
argument :limit, Int, required: false, default_value: 10
argument :offset, Int, required: false, default_value: 0
end
field :comment_count, Int, null: false
def self.resolve_type(object, context)
"#{object.class.name}Type".constantize
end
end
# Object types implementing interfaces
class UserType < GraphQL::Schema::Object
implements NodeInterface, TimestampedInterface
field :name, String, null: false
field :email, String, null: false
# id, created_at, updated_at inherited from interfaces
end
class PostType < GraphQL::Schema::Object
implements NodeInterface, TimestampedInterface, CommentableInterface
field :title, String, null: false
field :content, String, null: true
field :author, UserType, null: false
# Implementation of CommentableInterface
def comments(limit: 10, offset: 0)
object.comments.limit(limit).offset(offset)
end
def comment_count
object.comments.count
end
end
# Querying with interfaces
query = '
query {
node(id: "Post:123") {
id
... on Timestamped {
createdAt
updatedAt
}
... on Commentable {
commentCount
comments(limit: 5) {
id
body
}
}
... on Post {
title
content
}
}
}
'Define list types and non-null constraints for fields and arguments.
# List wrapper
class GraphQL::Schema::List
def initialize(of_type)
def of_type
end
# Non-null wrapper
class GraphQL::Schema::NonNull
def initialize(of_type)
def of_type
end
# Shorthand syntax in field definitions
field :name, String, null: false # NonNull(String)
field :tags, [String], null: false # NonNull(List(String))
field :tags, [String], null: true # List(String)
field :tags, [String!], null: false # NonNull(List(NonNull(String)))
field :tags, [String!], null: true # List(NonNull(String))Usage Examples:
class UserType < GraphQL::Schema::Object
# Required single values
field :id, ID, null: false # Must return an ID
field :email, String, null: false # Must return a String
# Optional single values
field :name, String, null: true # Can return String or null
field :bio, String, null: true # Can return String or null
# Required lists
field :roles, [String], null: false # Must return array (can be empty)
field :permissions, [String!], null: false # Must return array of non-null strings
# Optional lists
field :hobbies, [String], null: true # Can return array or null
field :skills, [String!], null: true # Can return array of non-null strings or null
# Complex nested types
field :posts, [PostType!], null: false do # Must return array of non-null posts
argument :status, PostStatusEnum, required: false
argument :limit, Int, required: false, default_value: 10
end
def posts(status: nil, limit: 10)
scope = object.posts
scope = scope.where(status: status) if status
scope.limit(limit).to_a # Convert to array to ensure it's never null
end
end
# Input objects with list constraints
class UpdateUserInput < GraphQL::Schema::InputObject
argument :name, String, required: false # Optional string
argument :tags, [String!], required: false # Optional array of required strings
argument :role_ids, [ID!], required: true # Required array of required IDs
end