The Type class in lib/rails/graphql/type.rb defines the base type hierarchy:
TYPE::KINDS = %w[Scalar Object Interface Union Enum Input].freeze
Each type has methods to identify its characteristics:
type.kind # Returns :scalar, :object, :interface, etc.type.kind_enum # Returns "SCALAR", "OBJECT", "INTERFACE", etc.type.input_type? # Is it a valid input type?type.output_type? # Is it a valid output type?type.leaf_type? # Is it a leaf type (scalar/enum)?
Objects are the most common type, organizing fields into logical groups:
module GraphQL class User < GraphQL::Object desc 'A user in the system' field :id, :id, null: false field :name, :string field :email, :string field :role, 'Role' endend
Interfaces define common fields that multiple objects must implement:
app/graphql/interfaces/node.rb
module GraphQL class Node < GraphQL::Interface desc 'An object with a unique ID' field :id, :id, null: false # Define how to resolve concrete types def self.type_for(value, request) request.find_type(value.class.name) end endend
Unions represent a type that could be one of several object types:
app/graphql/unions/search_result.rb
module GraphQL class SearchResult < GraphQL::Union desc 'A search result can be a User or Post' append User, Post # Define how to resolve concrete types def self.type_for(value, request) case value when ::User then request.find_type(:user) when ::Post then request.find_type(:post) end end endend
module GraphQL class Role < GraphQL::Enum desc 'User role in the system' add 'ADMIN', desc: 'Administrator with full access' add 'USER', desc: 'Regular user' add 'GUEST', desc: 'Guest with limited access' endend
module GraphQL class UserInput < GraphQL::Input desc 'Input for creating or updating users' field :name, :string, null: false field :email, :string, null: false field :role, 'Role', default: 'USER' endend
Input instances provide convenient methods:
def create_user(user:) # user is a UserInput instance user.args.name # Access individual fields user.params # Get sanitized hash user.resource # Get assigned resource (if configured)end
Create custom scalars by defining transformation methods:
app/graphql/scalars/email_scalar.rb
module GraphQL class EmailScalar < GraphQL::Scalar desc 'A valid email address' class << self def valid_input?(value) value.is_a?(String) && value.match?(URI::MailTo::EMAIL_REGEXP) end def valid_output?(value) value.is_a?(String) end def to_json(value) value.to_json end def as_json(value) value.to_s end def deserialize(value) value.to_s.downcase end end endend
Every type has specific characteristics defined by the base class:
# Check type classificationtype.scalar? # Is it a scalar?type.object? # Is it an object?type.interface? # Is it an interface?type.union? # Is it a union?type.enum? # Is it an enum?type.input? # Is it an input?# Check type capabilitiestype.input_type? # Can it be used as input?type.output_type? # Can it be used as output?type.leaf_type? # Is it a leaf (scalar/enum)?
Schemas provide helper methods for creating types inline:
module GraphQL class AppSchema < GraphQL::Schema # Each type kind has a helper method scalar 'CustomScalar' object 'User' do field :id end interface 'Node' union 'Result', of_types: %w[User Post] enum 'Status', values: %i[active inactive] input 'UserInput' do field :name end endend
These methods use the Type::Creator class to dynamically generate type classes at runtime.