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

query-execution.mddocs/

Query Execution

Query parsing, execution engine, and context management for running GraphQL operations against schemas in GraphQL Ruby.

Capabilities

Query Parsing

Parse GraphQL query strings into Abstract Syntax Trees and validate syntax.

# Main parsing interface
def GraphQL.parse(graphql_string, trace: GraphQL::Tracing::NullTrace, filename: nil, max_tokens: nil)
def GraphQL.parse_file(filename)
def GraphQL.scan(graphql_string)

# Language parsing classes
class GraphQL::Language::Parser
  def self.parse(string, filename: nil, trace: GraphQL::Tracing::NullTrace)
end

class GraphQL::Language::Lexer  
  def self.tokenize(graphql_string)
end

# Parse result
class GraphQL::Language::Nodes::Document
  def definitions
  def to_query_string
end

Usage Examples:

# Parse a query string
query_string = '
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
    }
  }
'

document = GraphQL.parse(query_string)
puts document.definitions.first.name  # "GetUser"

# Parse from file
document = GraphQL.parse_file("queries/get_user.graphql")

# Tokenize for analysis
tokens = GraphQL.scan(query_string)
tokens.each { |token| puts "#{token[0]}: #{token[1]}" }

Query Object

Represents a parsed GraphQL operation ready for execution with variables and context.

class GraphQL::Query
  def initialize(schema, query_string = nil, document: nil, variables: {}, context: {}, operation_name: nil, root_value: nil, max_depth: nil, max_complexity: nil)
  
  # Execution
  def result
  def result_name
  
  # Validation and analysis
  def valid?
  def static_errors
  def analyzers
  
  # Query introspection
  def operation_name
  def selected_operation
  def variables
  def context
  def root_value
  def schema
  def document
  def query_string
  
  # Utilities
  def fingerprint
  def sanitized_query_string(inline_variables: false)
end

Usage Examples:

# Create and execute a query
schema = MySchema
query_string = 'query { users { id name } }'

query = GraphQL::Query.new(
  schema,
  query_string,
  variables: {},
  context: { current_user: current_user },
  operation_name: nil
)

# Check if query is valid
unless query.valid?
  puts "Query errors:"
  query.static_errors.each { |error| puts error.message }
end

# Execute the query
result = query.result
puts result.to_json

# Query introspection
puts "Operation: #{query.operation_name}"
puts "Variables: #{query.variables}"
puts "Fingerprint: #{query.fingerprint}"

Query Result

The result of executing a GraphQL query, containing data and errors.

class GraphQL::Query::Result
  # Data access
  def [] (key)
  def dig(*keys)
  def to_h
  def to_json(**options)
  
  # Error handling
  def context_errors
  def execution_errors
  
  # Metadata
  def query
  def extensions
end

Usage Examples:

result = MySchema.execute('query { user(id: "1") { name email } }')

# Access data
user_data = result["data"]["user"]
user_name = result.dig("data", "user", "name")

# Check for errors
if result["errors"]&.any?
  puts "GraphQL errors:"
  result["errors"].each { |error| puts error["message"] }
end

# Convert to JSON for API response
json_response = result.to_json

# Access extensions (custom metadata)
if result["extensions"]
  puts "Query complexity: #{result['extensions']['complexity']}"
end

Context Management

Pass request-scoped data through the GraphQL execution pipeline.

class GraphQL::Query::Context
  def initialize(query:, schema:, values: {})
  
  # Data access
  def [] (key)
  def []= (key, value)
  def key?(key)
  def fetch(key, default = nil)
  def delete(key)
  def merge(new_values)
  def merge!(new_values)
  
  # Namespace support
  def namespace(ns_name)
  
  # Query access
  def query
  def schema
  def warden
  def interpreter?
  
  # Execution state
  def errors
  def add_error(error)
  def invalid_null?
  def skip
  def ast_node
  def irep_node
  def path
end

Usage Examples:

# Setting up context in schema execution
result = MySchema.execute(
  query_string,
  context: {
    current_user: current_user,
    request: request,
    abilities: current_user.abilities,
    dataloader: MyDataLoader.new
  }
)

# Accessing context in resolvers
class UserType < GraphQL::Schema::Object
  field :posts, [PostType], null: true
  
  def posts
    current_user = context[:current_user]
    abilities = context[:abilities]
    
    # Filter posts based on user permissions
    object.posts.accessible_by(abilities)
  end
end

# Adding errors to context
def secure_field
  unless context[:current_user]&.admin?
    context.add_error(GraphQL::ExecutionError.new(
      "Access denied",
      path: context.path,
      extensions: { code: "UNAUTHORIZED" }
    ))
    return nil
  end
  
  # Return authorized data
  object.secure_data
end

# Using namespaced context
def expensive_computation
  cache = context.namespace(:cache)
  cache[:computation_result] ||= perform_expensive_operation
end

Execution Control

Control query execution behavior including multiplexing, timeouts, and analysis.

class GraphQL::Schema
  # Single query execution
  def self.execute(query_string, variables: {}, context: {}, operation_name: nil, root_value: nil, only: nil, except: nil, validate: true)
  
  # Multiple query execution (multiplexing)
  def self.multiplex(queries, context: {}, max_complexity: nil, validate: true)
  
  # Analysis configuration
  def self.query_analyzer(analyzer_class)
  def self.analysis_engine(engine = nil)
  
  # Execution hooks
  def self.lazy_resolve(lazy_class, method_name)
  def self.after_lazy(value, &block)
end

# Multiplex execution
class GraphQL::Execution::Multiplex
  def initialize(schema:, queries:, context: {})
  def self.run_all(schema, query_options, **kwargs)
end

Usage Examples:

# Single query with options
result = MySchema.execute(
  query_string,
  variables: { id: "123" },
  context: { current_user: user },
  operation_name: "GetUser",
  only: [:query],  # Only allow queries, not mutations
  validate: true   # Validate before execution
)

# Multiplex execution (multiple queries in one request)
queries = [
  {
    query: "query GetUser($id: ID!) { user(id: $id) { name } }",
    variables: { id: "1" },
    operation_name: "GetUser"
  },
  {
    query: "query GetPosts { posts(limit: 5) { title } }",
    operation_name: "GetPosts"
  }
]

results = MySchema.multiplex(queries, context: { current_user: user })
results.each_with_index do |result, index|
  puts "Query #{index + 1}: #{result.to_json}"
end

# Custom lazy resolution
class MySchema < GraphQL::Schema
  # Define how to resolve lazy values
  lazy_resolve(Promise, :sync)
  lazy_resolve(Concurrent::Future, :value!)
end

def lazy_field
  # Return a lazy value that will be resolved later
  Promise.new { expensive_database_call }
end

Error Handling

Handle and customize GraphQL execution errors.

class GraphQL::ExecutionError < GraphQL::Error
  def initialize(message, ast_node: nil, options: {})
  
  # Error information
  def message
  def path
  def locations
  def ast_node
  def extensions
  
  # Extensions for additional error data
  def extensions=(new_extensions)
end

# Schema error handling hooks
class GraphQL::Schema
  def self.type_error(err, context)
  def self.rescue_from(error_class, &block)
  def self.unauthorized_object(error)
  def self.unauthorized_field(error)
end

Usage Examples:

# Raising execution errors in resolvers
def user
  user = User.find_by(id: args[:id])
  
  unless user
    raise GraphQL::ExecutionError.new(
      "User not found",
      extensions: { 
        code: "NOT_FOUND",
        timestamp: Time.current.iso8601
      }
    )
  end
  
  user
end

# Schema-level error handling
class MySchema < GraphQL::Schema
  # Handle type resolution errors
  def self.type_error(err, context)
    GraphQL::ExecutionError.new(
      "Failed to resolve type: #{err.message}",
      extensions: { code: "TYPE_ERROR" }
    )
  end
  
  # Handle specific exception types
  rescue_from(ActiveRecord::RecordNotFound) do |err, obj, args, ctx, field|
    GraphQL::ExecutionError.new(
      "#{field.owner.name} not found",
      extensions: { code: "NOT_FOUND" }
    )
  end
  
  rescue_from(ActiveRecord::RecordInvalid) do |err, obj, args, ctx, field|
    GraphQL::ExecutionError.new(
      "Validation failed: #{err.record.errors.full_messages.join(', ')}",
      extensions: { 
        code: "VALIDATION_ERROR",
        details: err.record.errors.messages
      }
    )
  end
  
  # Handle authorization errors
  def self.unauthorized_object(err)
    GraphQL::ExecutionError.new(
      "Access denied",
      extensions: { code: "UNAUTHORIZED" }
    )
  end
end

# Collecting multiple errors
def resolve_with_multiple_errors
  errors = []
  results = []
  
  args[:ids].each do |id|
    begin
      results << fetch_item(id)
    rescue StandardError => e
      errors << GraphQL::ExecutionError.new(
        e.message,
        path: context.path + [results.length],
        extensions: { code: "FETCH_ERROR" }
      )
      results << nil
    end
  end
  
  errors.each { |error| context.add_error(error) }
  results
end