or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

dataloader-performance.mdindex.mdquery-execution.mdschema-definition.mdtype-system.md
tile.json

type-system.mddocs/

Type System

Built-in scalar types, custom scalars, enums, interfaces, unions, and input objects that form GraphQL Ruby's type system.

Capabilities

Built-in Scalar Types

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 format

Usage 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
    }
  }
'

Custom Scalar Types

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

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

Enum Types

Define 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)
end

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

Union Types

Define 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
      }
    }
  }
'

Interface Types

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

Usage 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
      }
    }
  }
'

List and Non-Null Wrappers

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