class GraphQL::Schema

A GraphQL schema which may be queried with {GraphQL::Query}.

The {Schema} contains:

- types for exposing your application
- query analyzers for assessing incoming queries (including max depth & max complexity restrictions)
- execution strategies for running incoming queries

Schemas start with root types, {Schema#query}, {Schema#mutation} and {Schema#subscription}. The schema will traverse the tree of fields & types, using those as starting points. Any undiscoverable types may be provided with the ‘types` configuration.

Schemas can restrict large incoming queries with ‘max_depth` and `max_complexity` configurations. (These configurations can be overridden by specific calls to {Schema#execute})

Schemas can specify how queries should be executed against them. ‘query_execution_strategy`, `mutation_execution_strategy` and `subscription_execution_strategy` each apply to corresponding root types.

#

@example defining a schema

class MySchema < GraphQL::Schema
  query QueryType
  # If types are only connected by way of interfaces, they must be added here
  orphan_types ImageType, AudioType
end

Extend this class to define GraphQL enums in your schema.

By default, GraphQL enum values are translated into Ruby strings. You can provide a custom value with the ‘value:` keyword.

@example

# equivalent to
# enum PizzaTopping {
#   MUSHROOMS
#   ONIONS
#   PEPPERS
# }
class PizzaTopping < GraphQL::Enum
  value :MUSHROOMS
  value :ONIONS
  value :PEPPERS
end

Constants

BUILT_IN_TYPES
DYNAMIC_FIELDS

Attributes

analysis_engine[W]
connections[W]

@api private

dataloader_class[W]
default_execution_strategy[W]
error_bubbling[W]
interpreter[W]
max_complexity[W]
max_depth[W]
subscriptions[RW]

@return [GraphQL::Subscriptions]

validate_max_errors[W]
validate_timeout[W]
analysis_engine[RW]
ast_node[RW]
connections[RW]
context_class[RW]

@see {GraphQL::Query::Context} The parent class of these classes @return [Class] Instantiated for each query

cursor_encoder[RW]
default_mask[RW]

@return [<#call(member, ctx)>] A callable for filtering members of the schema @see {Query.new} for query-specific filters with ‘except:`

default_max_page_size[RW]
directives[RW]
disable_introspection_entry_points[RW]
Boolean

True if this object disables the introspection entry point fields

disable_schema_introspection_entry_point[RW]
Boolean

True if this object disables the __schema introspection entry point field

disable_type_introspection_entry_point[RW]
Boolean

True if this object disables the __type introspection entry point field

error_bubbling[RW]
Boolean

True if this object bubbles validation errors up from a field into its parent InputObject, if there is one.

id_from_object_proc[R]
instrumenters[RW]
introspection_namespace[RW]
lazy_methods[RW]
max_complexity[RW]
max_depth[RW]
middleware[RW]

@return [MiddlewareChain] MiddlewareChain which is applied to fields during execution

multiplex_analyzers[RW]
mutation[RW]
mutation_execution_strategy[RW]
object_from_id_proc[R]
orphan_types[RW]
query[RW]
query_analyzers[RW]
query_execution_strategy[RW]
raise_definition_error[RW]
resolve_type_proc[R]
static_validator[R]
subscription[RW]
subscription_execution_strategy[RW]
subscriptions[RW]

Single, long-lived instance of the provided subscriptions class, if there is one. @return [GraphQL::Subscriptions]

tracers[R]

@return [Array<#trace(key, data)>] Tracers applied to every query @see {Query#tracers} for query-specific tracers

validate_max_errors[RW]
validate_timeout[RW]

Public Class Methods

accessible?(member, ctx) click to toggle source
# File lib/graphql/schema.rb, line 1563
def accessible?(member, ctx)
  member.type_class.accessible?(ctx)
end
add_subscription_extension_if_necessary() click to toggle source

@api private

# File lib/graphql/schema.rb, line 1779
def add_subscription_extension_if_necessary
  if interpreter? && !defined?(@subscription_extension_added) && subscription && self.subscriptions
    @subscription_extension_added = true
    if subscription.singleton_class.ancestors.include?(Subscriptions::SubscriptionRoot)
      GraphQL::Deprecation.warn("`extend Subscriptions::SubscriptionRoot` is no longer required; you may remove it from #{self}'s `subscription` root type (#{subscription}).")
    else
      subscription.all_field_definitions.each do |field|
        field.extension(Subscriptions::DefaultSubscriptionResolveExtension)
      end
    end
  end
end
analysis_engine() click to toggle source
# File lib/graphql/schema.rb, line 1391
def analysis_engine
  @analysis_engine || find_inherited_value(:analysis_engine, self.default_analysis_engine)
end
as_json(only: nil, except: nil, context: {}) click to toggle source

Return the Hash response of {Introspection::INTROSPECTION_QUERY}. @param context [Hash] @param only [<#call(member, ctx)>] @param except [<#call(member, ctx)>] @return [Hash] GraphQL result

# File lib/graphql/schema.rb, line 876
def as_json(only: nil, except: nil, context: {})
  execute(Introspection.query(include_deprecated_args: true), only: only, except: except, context: context).to_h
end
connections() click to toggle source

@return [GraphQL::Pagination::Connections] if installed

# File lib/graphql/schema.rb, line 1082
def connections
  if defined?(@connections)
    @connections
  else
    inherited_connections = find_inherited_value(:connections, nil)
    # This schema is part of an inheritance chain which is using new connections,
    # make a new instance, so we don't pollute the upstream one.
    if inherited_connections
      @connections = Pagination::Connections.new(schema: self)
    else
      nil
    end
  end
end
context_class(new_context_class = nil) click to toggle source
# File lib/graphql/schema.rb, line 1497
def context_class(new_context_class = nil)
  if new_context_class
    @context_class = new_context_class
  else
    @context_class || find_inherited_value(:context_class, GraphQL::Query::Context)
  end
end
cursor_encoder(new_encoder = nil) click to toggle source
# File lib/graphql/schema.rb, line 1297
def cursor_encoder(new_encoder = nil)
  if new_encoder
    @cursor_encoder = new_encoder
  end
  @cursor_encoder || find_inherited_value(:cursor_encoder, Base64Encoder)
end
dataloader_class() click to toggle source

@api private @see GraphQL::Dataloader

# File lib/graphql/schema.rb, line 1214
def dataloader_class
  @dataloader_class || GraphQL::Dataloader::NullDataloader
end
default_analysis_engine() click to toggle source
# File lib/graphql/schema.rb, line 1489
def default_analysis_engine
  if superclass <= GraphQL::Schema
    superclass.default_analysis_engine
  else
    @default_analysis_engine ||= GraphQL::Analysis::AST
  end
end
default_directives() click to toggle source
# File lib/graphql/schema.rb, line 1668
def default_directives
  @default_directives ||= {
    "include" => GraphQL::Schema::Directive::Include,
    "skip" => GraphQL::Schema::Directive::Skip,
    "deprecated" => GraphQL::Schema::Directive::Deprecated,
  }.freeze
end
default_execution_strategy() click to toggle source
# File lib/graphql/schema.rb, line 1481
def default_execution_strategy
  if superclass <= GraphQL::Schema
    superclass.default_execution_strategy
  else
    @default_execution_strategy ||= GraphQL::Execution::Interpreter
  end
end
default_filter() click to toggle source
# File lib/graphql/schema.rb, line 925
def default_filter
  GraphQL::Filter.new(except: default_mask)
end
default_mask(new_mask = nil) click to toggle source
# File lib/graphql/schema.rb, line 929
def default_mask(new_mask = nil)
  if new_mask
    @own_default_mask = new_mask
  else
    @own_default_mask || find_inherited_value(:default_mask, Schema::NullMask)
  end
end
default_max_page_size(new_default_max_page_size = nil) click to toggle source
# File lib/graphql/schema.rb, line 1304
def default_max_page_size(new_default_max_page_size = nil)
  if new_default_max_page_size
    @default_max_page_size = new_default_max_page_size
  else
    @default_max_page_size || find_inherited_value(:default_max_page_size)
  end
end
deprecated_graphql_definition() click to toggle source
# File lib/graphql/schema.rb, line 857
def deprecated_graphql_definition
  graphql_definition(silence_deprecation_warning: true)
end
description(new_description = nil) click to toggle source

@return [String, nil]

# File lib/graphql/schema.rb, line 896
def description(new_description = nil)
  if new_description
    @description = new_description
  elsif defined?(@description)
    @description
  else
    find_inherited_value(:description, nil)
  end
end
directive(new_directive) click to toggle source

Attach a single directive to this schema @param new_directive [Class] @return void

# File lib/graphql/schema.rb, line 1664
def directive(new_directive)
  add_type_and_traverse(new_directive, root: false)
end
directives(*new_directives) click to toggle source

Add several directives at once @param new_directives [Class]

# File lib/graphql/schema.rb, line 1653
def directives(*new_directives)
  if new_directives.any?
    new_directives.flatten.each { |d| directive(d) }
  end

  find_inherited_value(:directives, default_directives).merge(own_directives)
end
disable_introspection_entry_points() click to toggle source
# File lib/graphql/schema.rb, line 1429
def disable_introspection_entry_points
  @disable_introspection_entry_points = true
  # TODO: this clears the cache made in `def types`. But this is not a great solution.
  @introspection_system = nil
end
disable_introspection_entry_points?() click to toggle source
# File lib/graphql/schema.rb, line 1447
def disable_introspection_entry_points?
  if instance_variable_defined?(:@disable_introspection_entry_points)
    @disable_introspection_entry_points
  else
    find_inherited_value(:disable_introspection_entry_points?, false)
  end
end
disable_schema_introspection_entry_point() click to toggle source
# File lib/graphql/schema.rb, line 1435
def disable_schema_introspection_entry_point
  @disable_schema_introspection_entry_point = true
  # TODO: this clears the cache made in `def types`. But this is not a great solution.
  @introspection_system = nil
end
disable_schema_introspection_entry_point?() click to toggle source
# File lib/graphql/schema.rb, line 1455
def disable_schema_introspection_entry_point?
  if instance_variable_defined?(:@disable_schema_introspection_entry_point)
    @disable_schema_introspection_entry_point
  else
    find_inherited_value(:disable_schema_introspection_entry_point?, false)
  end
end
disable_type_introspection_entry_point() click to toggle source
# File lib/graphql/schema.rb, line 1441
def disable_type_introspection_entry_point
  @disable_type_introspection_entry_point = true
  # TODO: this clears the cache made in `def types`. But this is not a great solution.
  @introspection_system = nil
end
disable_type_introspection_entry_point?() click to toggle source
# File lib/graphql/schema.rb, line 1463
def disable_type_introspection_entry_point?
  if instance_variable_defined?(:@disable_type_introspection_entry_point)
    @disable_type_introspection_entry_point
  else
    find_inherited_value(:disable_type_introspection_entry_point?, false)
  end
end
error_bubbling(new_error_bubbling = nil) click to toggle source
# File lib/graphql/schema.rb, line 1407
def error_bubbling(new_error_bubbling = nil)
  if !new_error_bubbling.nil?
    @error_bubbling = new_error_bubbling
  else
    @error_bubbling.nil? ? find_inherited_value(:error_bubbling) : @error_bubbling
  end
end
error_handler() click to toggle source

@return [GraphQL::Execution::Errors]

# File lib/graphql/schema.rb, line 1629
def error_handler
  @error_handler ||= GraphQL::Execution::Errors.new(self)
end
execute(query_str = nil, **kwargs) click to toggle source

Execute a query on itself. @see {Query#initialize} for arguments. @return [Hash] query result, ready to be serialized as JSON

# File lib/graphql/schema.rb, line 1724
def execute(query_str = nil, **kwargs)
  if query_str
    kwargs[:query] = query_str
  end
  # Some of the query context _should_ be passed to the multiplex, too
  multiplex_context = if (ctx = kwargs[:context])
    {
      backtrace: ctx[:backtrace],
      tracers: ctx[:tracers],
      dataloader: ctx[:dataloader],
    }
  else
    {}
  end
  # Since we're running one query, don't run a multiplex-level complexity analyzer
  all_results = multiplex([kwargs], max_complexity: nil, context: multiplex_context)
  all_results[0]
end
find(path) click to toggle source
# File lib/graphql/schema.rb, line 906
def find(path)
  if !@finder
    @find_cache = {}
    @finder ||= GraphQL::Schema::Finder.new(self)
  end
  @find_cache[path] ||= @finder.find(path)
end
from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {}) click to toggle source

Create schema from an IDL schema or file containing an IDL definition. @param definition_or_path [String] A schema definition string, or a path to a file containing the definition @param default_resolve [<#call(type, field, obj, args, ctx)>] A callable for handling field resolution @param parser [Object] An object for handling definition string parsing (must respond to ‘parse`) @param using [Hash] Plugins to attach to the created schema with `use(key, value)` @return [Class] the schema described by `document`

# File lib/graphql/schema.rb, line 780
def self.from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {})
  # If the file ends in `.graphql`, treat it like a filepath
  if definition_or_path.end_with?(".graphql")
    GraphQL::Schema::BuildFromDefinition.from_definition_path(
      definition_or_path,
      default_resolve: default_resolve,
      parser: parser,
      using: using,
    )
  else
    GraphQL::Schema::BuildFromDefinition.from_definition(
      definition_or_path,
      default_resolve: default_resolve,
      parser: parser,
      using: using,
    )
  end
end
from_introspection(introspection_result) click to toggle source

Create schema with the result of an introspection query. @param introspection_result [Hash] A response from {GraphQL::Introspection::INTROSPECTION_QUERY} @return [GraphQL::Schema] the schema described by ‘input`

# File lib/graphql/schema.rb, line 770
def self.from_introspection(introspection_result)
  GraphQL::Schema::Loader.load(introspection_result)
end
get_field(type_or_name, field_name, context = GraphQL::Query::NullContext) click to toggle source
# File lib/graphql/schema.rb, line 1252
def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext)
  parent_type = case type_or_name
  when LateBoundType
    get_type(type_or_name.name, context)
  when String
    get_type(type_or_name, context)
  when Module
    type_or_name
  else
    raise GraphQL::InvariantError, "Unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
  end

  if parent_type.kind.fields? && (field = parent_type.get_field(field_name, context))
    field
  elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name))
    entry_point_field
  elsif (dynamic_field = introspection_system.dynamic_field(name: field_name))
    dynamic_field
  else
    nil
  end
end
get_fields(type, context = GraphQL::Query::NullContext) click to toggle source
# File lib/graphql/schema.rb, line 1275
def get_fields(type, context = GraphQL::Query::NullContext)
  type.fields(context)
end
get_type(type_name, context = GraphQL::Query::NullContext) click to toggle source

@param type_name [String] @return [Module, nil] A type, or nil if there’s no type called ‘type_name`

# File lib/graphql/schema.rb, line 1049
def get_type(type_name, context = GraphQL::Query::NullContext)
  local_entry = own_types[type_name]
  type_defn = case local_entry
  when nil
    nil
  when Array
    visible_t = nil
    warden = Warden.from_context(context)
    local_entry.each do |t|
      if warden.visible_type?(t, context)
        if visible_t.nil?
          visible_t = t
        else
          raise DuplicateNamesError, "Found two visible type definitions for `#{type_name}`: #{visible_t.inspect}, #{t.inspect}"
        end
      end
    end
    visible_t
  when Module
    local_entry
  else
    raise "Invariant: unexpected own_types[#{type_name.inspect}]: #{local_entry.inspect}"
  end

  type_defn ||
    introspection_system.types[type_name] || # todo context-specific introspection?
    (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
end
graphql_definition(silence_deprecation_warning: false) click to toggle source
# File lib/graphql/schema.rb, line 914
def graphql_definition(silence_deprecation_warning: false)
  @graphql_definition ||= begin
    unless silence_deprecation_warning
      message = "Legacy `.graphql_definition` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Use a class-based definition instead."
      caller_message = "\n\nCalled on #{self.inspect} from:\n #{caller(1, 25).map { |l| "  #{l}" }.join("\n")}"
      GraphQL::Deprecation.warn(message + caller_message)
    end
    to_graphql(silence_deprecation_warning: silence_deprecation_warning)
  end
end
id_from_object(object, type, ctx) click to toggle source
# File lib/graphql/schema.rb, line 1555
def id_from_object(object, type, ctx)
  raise GraphQL::RequiredImplementationMissingError, "#{self.name}.id_from_object(object, type, ctx) must be implemented to create global ids (tried to create an id for `#{object.inspect}`)"
end
inaccessible_fields(error) click to toggle source

This hook is called when a client tries to access one or more fields that fail the ‘accessible?` check.

By default, an error is added to the response. Override this hook to track metrics or return a different error to the client.

@param error [InaccessibleFieldsError] The analysis error for this check @return [AnalysisError, nil] Return an error to skip the query

# File lib/graphql/schema.rb, line 1575
def inaccessible_fields(error)
  error
end
inherited(child_class) click to toggle source

rubocop:enable Lint/DuplicateMethods

Calls superclass method
# File lib/graphql/schema.rb, line 1543
def inherited(child_class)
  if self == GraphQL::Schema
    child_class.directives(default_directives.values)
  end
  child_class.singleton_class.prepend(ResolveTypeWithType)
  super
end
instrument(instrument_step, instrumenter, options = {}) click to toggle source
# File lib/graphql/schema.rb, line 1637
def instrument(instrument_step, instrumenter, options = {})
  if instrument_step == :field
    GraphQL::Deprecation.warn "Field instrumentation (#{instrumenter.inspect}) will be removed in GraphQL-Ruby 2.0, please upgrade to field extensions: https://graphql-ruby.org/type_definitions/field_extensions.html"
  end

  step = if instrument_step == :field && options[:after_built_ins]
    :field_after_built_ins
  else
    instrument_step
  end

  own_instrumenters[step] << instrumenter
end
instrumenters() click to toggle source
# File lib/graphql/schema.rb, line 1771
def instrumenters
  inherited_instrumenters = find_inherited_value(:instrumenters) || Hash.new { |h,k| h[k] = [] }
  inherited_instrumenters.merge(own_instrumenters) do |_step, inherited, own|
    inherited + own
  end
end
interpreter?() click to toggle source
# File lib/graphql/schema.rb, line 1399
def interpreter?
  query_execution_strategy == GraphQL::Execution::Interpreter &&
    mutation_execution_strategy == GraphQL::Execution::Interpreter &&
    subscription_execution_strategy == GraphQL::Execution::Interpreter
end
introspection(new_introspection_namespace = nil) click to toggle source
# File lib/graphql/schema.rb, line 1279
def introspection(new_introspection_namespace = nil)
  if new_introspection_namespace
    @introspection = new_introspection_namespace
    # reset this cached value:
    @introspection_system = nil
  else
    @introspection || find_inherited_value(:introspection)
  end
end
introspection_system() click to toggle source
# File lib/graphql/schema.rb, line 1289
def introspection_system
  if !@introspection_system
    @introspection_system = Schema::IntrospectionSystem.new(self)
    @introspection_system.resolve_late_bindings
  end
  @introspection_system
end
lazy_resolve(lazy_class, value_method) click to toggle source
# File lib/graphql/schema.rb, line 1633
def lazy_resolve(lazy_class, value_method)
  lazy_methods.set(lazy_class, value_method)
end
max_complexity(max_complexity = nil) click to toggle source
# File lib/graphql/schema.rb, line 1379
def max_complexity(max_complexity = nil)
  if max_complexity
    @max_complexity = max_complexity
  elsif defined?(@max_complexity)
    @max_complexity
  else
    find_inherited_value(:max_complexity)
  end
end
max_depth(new_max_depth = nil) click to toggle source
# File lib/graphql/schema.rb, line 1419
def max_depth(new_max_depth = nil)
  if new_max_depth
    @max_depth = new_max_depth
  elsif defined?(@max_depth)
    @max_depth
  else
    find_inherited_value(:max_depth)
  end
end
middleware(new_middleware = nil) click to toggle source
# File lib/graphql/schema.rb, line 1695
def middleware(new_middleware = nil)
  if new_middleware
    GraphQL::Deprecation.warn "Middleware will be removed in GraphQL-Ruby 2.0, please upgrade to Field Extensions: https://graphql-ruby.org/type_definitions/field_extensions.html"
    own_middleware << new_middleware
  else
    # TODO make sure this is cached when running a query
    MiddlewareChain.new(steps: all_middleware, final_step: GraphQL::Execution::Execute::FieldResolveStep)
  end
end
multiplex(queries, **kwargs) click to toggle source

Execute several queries on itself, concurrently.

@example Run several queries at once

context = { ... }
queries = [
  { query: params[:query_1], variables: params[:variables_1], context: context },
  { query: params[:query_2], variables: params[:variables_2], context: context },
]
results = MySchema.multiplex(queries)
render json: {
  result_1: results[0],
  result_2: results[1],
}

@see {Query#initialize} for query keyword arguments @see {Execution::Multiplex#run_queries} for multiplex keyword arguments @param queries [Array<Hash>] Keyword arguments for each query @param context [Hash] Multiplex-level context @return [Array<Hash>] One result for each query in the input

# File lib/graphql/schema.rb, line 1762
def multiplex(queries, **kwargs)
  schema = if interpreter?
    self
  else
    graphql_definition
  end
  GraphQL::Execution::Multiplex.run_all(schema, queries, **kwargs)
end
multiplex_analyzer(new_analyzer) click to toggle source
# File lib/graphql/schema.rb, line 1705
def multiplex_analyzer(new_analyzer)
  own_multiplex_analyzers << new_analyzer
end
multiplex_analyzers() click to toggle source
# File lib/graphql/schema.rb, line 1709
def multiplex_analyzers
  find_inherited_value(:multiplex_analyzers, EMPTY_ARRAY) + own_multiplex_analyzers
end
mutation(new_mutation_object = nil) click to toggle source
# File lib/graphql/schema.rb, line 1115
def mutation(new_mutation_object = nil)
  if new_mutation_object
    if @mutation_object
      raise GraphQL::Error, "Second definition of `mutation(...)` (#{new_mutation_object.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
    else
      @mutation_object = new_mutation_object
      add_type_and_traverse(new_mutation_object, root: true)
      nil
    end
  else
    @mutation_object || find_inherited_value(:mutation)
  end
end
mutation_execution_strategy(new_mutation_execution_strategy = nil) click to toggle source
# File lib/graphql/schema.rb, line 1320
def mutation_execution_strategy(new_mutation_execution_strategy = nil)
  if new_mutation_execution_strategy
    @mutation_execution_strategy = new_mutation_execution_strategy
  else
    @mutation_execution_strategy || find_inherited_value(:mutation_execution_strategy, self.default_execution_strategy)
  end
end
new() click to toggle source
# File lib/graphql/schema.rb, line 269
def initialize
  @tracers = []
  @definition_error = nil
  @orphan_types = []
  @directives = {}
  self.class.default_directives.each do |name, dir|
    @directives[name] = dir.graphql_definition
  end
  @static_validator = GraphQL::StaticValidation::Validator.new(schema: self)
  @middleware = MiddlewareChain.new(final_step: GraphQL::Execution::Execute::FieldResolveStep)
  @query_analyzers = []
  @multiplex_analyzers = []
  @resolve_type_proc = nil
  @object_from_id_proc = nil
  @id_from_object_proc = nil
  @type_error_proc = DefaultTypeError
  @parse_error_proc = DefaultParseError
  @instrumenters = Hash.new { |h, k| h[k] = [] }
  @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
  @lazy_methods.set(GraphQL::Execution::Lazy, :value)
  @cursor_encoder = Base64Encoder
  # For schema instances, default to legacy runtime modules
  @analysis_engine = GraphQL::Analysis
  @query_execution_strategy = GraphQL::Execution::Execute
  @mutation_execution_strategy = GraphQL::Execution::Execute
  @subscription_execution_strategy = GraphQL::Execution::Execute
  @default_mask = GraphQL::Schema::NullMask
  @rebuilding_artifacts = false
  @context_class = GraphQL::Query::Context
  @introspection_namespace = nil
  @introspection_system = nil
  @interpreter = false
  @error_bubbling = false
  @disable_introspection_entry_points = false
  @disable_schema_introspection_entry_point = false
  @disable_type_introspection_entry_point = false
end
new_connections?() click to toggle source
# File lib/graphql/schema.rb, line 1097
def new_connections?
  !!connections
end
object_from_id(node_id, ctx) click to toggle source
# File lib/graphql/schema.rb, line 1551
def object_from_id(node_id, ctx)
  raise GraphQL::RequiredImplementationMissingError, "#{self.name}.object_from_id(node_id, ctx) must be implemented to load by ID (tried to load from id `#{node_id}`)"
end
orphan_types(*new_orphan_types) click to toggle source
# File lib/graphql/schema.rb, line 1471
def orphan_types(*new_orphan_types)
  if new_orphan_types.any?
    new_orphan_types = new_orphan_types.flatten
    add_type_and_traverse(new_orphan_types, root: false)
    own_orphan_types.concat(new_orphan_types.flatten)
  end

  find_inherited_value(:orphan_types, EMPTY_ARRAY) + own_orphan_types
end
parse_error(parse_err, ctx) click to toggle source

A function to call when {#execute} receives an invalid query string

The default is to add the error to ‘context.errors` @param err [GraphQL::ParseError] The error encountered during parsing @param ctx [GraphQL::Query::Context] The context for the query where the error occurred @return void

# File lib/graphql/schema.rb, line 1624
def parse_error(parse_err, ctx)
  ctx.errors.push(parse_err)
end
plugins() click to toggle source
# File lib/graphql/schema.rb, line 950
def plugins
  find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins
end
possible_types(type = nil, context = GraphQL::Query::NullContext) click to toggle source

@param type [Module] The type definition whose possible types you want to see @return [Hash<String, Module>] All possible types, if no ‘type` is given. @return [Array<Module>] Possible types for `type`, if it’s given.

# File lib/graphql/schema.rb, line 1166
def possible_types(type = nil, context = GraphQL::Query::NullContext)
  if type
    # TODO duck-typing `.possible_types` would probably be nicer here
    if type.kind.union?
      type.possible_types(context: context)
    else
      stored_possible_types = own_possible_types[type.graphql_name]
      visible_possible_types = if stored_possible_types && type.kind.interface?
        stored_possible_types.select do |possible_type|
          # Use `.graphql_name` comparison to match legacy vs class-based types.
          # When we don't need to support legacy `.define` types, use `.include?(type)` instead.
          possible_type.interfaces(context).any? { |interface| interface.graphql_name == type.graphql_name }
        end
      else
        stored_possible_types
      end
      visible_possible_types ||
        introspection_system.possible_types[type.graphql_name] ||
        (
          superclass.respond_to?(:possible_types) ?
            superclass.possible_types(type, context) :
            EMPTY_ARRAY
        )
    end
  else
    find_inherited_value(:possible_types, EMPTY_HASH)
      .merge(own_possible_types)
      .merge(introspection_system.possible_types)
  end
end
query(new_query_object = nil) click to toggle source
# File lib/graphql/schema.rb, line 1101
def query(new_query_object = nil)
  if new_query_object
    if @query_object
      raise GraphQL::Error, "Second definition of `query(...)` (#{new_query_object.inspect}) is invalid, already configured with #{@query_object.inspect}"
    else
      @query_object = new_query_object
      add_type_and_traverse(new_query_object, root: true)
      nil
    end
  else
    @query_object || find_inherited_value(:query)
  end
end
query_analyzer(new_analyzer) click to toggle source
# File lib/graphql/schema.rb, line 1684
def query_analyzer(new_analyzer)
  if new_analyzer == GraphQL::Authorization::Analyzer
    GraphQL::Deprecation.warn("The Authorization query analyzer is deprecated. Authorizing at query runtime is generally a better idea.")
  end
  own_query_analyzers << new_analyzer
end
query_analyzers() click to toggle source
# File lib/graphql/schema.rb, line 1691
def query_analyzers
  find_inherited_value(:query_analyzers, EMPTY_ARRAY) + own_query_analyzers
end
query_execution_strategy(new_query_execution_strategy = nil) click to toggle source
# File lib/graphql/schema.rb, line 1312
def query_execution_strategy(new_query_execution_strategy = nil)
  if new_query_execution_strategy
    @query_execution_strategy = new_query_execution_strategy
  else
    @query_execution_strategy || find_inherited_value(:query_execution_strategy, self.default_execution_strategy)
  end
end
query_stack_error(query, err) click to toggle source
# File lib/graphql/schema.rb, line 1792
def query_stack_error(query, err)
  query.context.errors.push(GraphQL::ExecutionError.new("This query is too large to execute."))
end
references_to(to_type = nil, from: nil) click to toggle source
# File lib/graphql/schema.rb, line 1220
def references_to(to_type = nil, from: nil)
  @own_references_to ||= Hash.new { |h, k| h[k] = [] }
  if to_type
    if !to_type.is_a?(String)
      to_type = to_type.graphql_name
    end

    if from
      @own_references_to[to_type] << from
    else
      own_refs = @own_references_to[to_type]
      inherited_refs = find_inherited_value(:references_to, EMPTY_HASH)[to_type] || EMPTY_ARRAY
      own_refs + inherited_refs
    end
  else
    # `@own_references_to` can be quite large for big schemas,
    # and generally speaking, we won't inherit any values.
    # So optimize the most common case -- don't create a duplicate Hash.
    inherited_value = find_inherited_value(:references_to, EMPTY_HASH)
    if inherited_value.any?
      inherited_value.merge(@own_references_to)
    else
      @own_references_to
    end
  end
end
rescue_from(*err_classes, &handler_block) click to toggle source
# File lib/graphql/schema.rb, line 1505
def rescue_from(*err_classes, &handler_block)
  err_classes.each do |err_class|
    error_handler.rescue_from(err_class, handler_block)
  end
end
resolve_type(type, obj, ctx) click to toggle source
# File lib/graphql/schema.rb, line 1534
def resolve_type(type, obj, ctx)
  if type.kind.object?
    type
  else
    raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(type, obj, ctx) must be implemented to use Union types or Interface types (tried to resolve: #{type.name})"
  end
end
root_type_for_operation(operation) click to toggle source

@see [GraphQL::Schema::Warden] Restricted access to root types @return [GraphQL::ObjectType, nil]

# File lib/graphql/schema.rb, line 1146
def root_type_for_operation(operation)
  case operation
  when "query"
    query
  when "mutation"
    mutation
  when "subscription"
    subscription
  else
    raise ArgumentError, "unknown operation type: #{operation}"
  end
end
root_types() click to toggle source
# File lib/graphql/schema.rb, line 1159
def root_types
  @root_types
end
sanitized_printer(new_sanitized_printer = nil) click to toggle source
# File lib/graphql/schema.rb, line 1713
def sanitized_printer(new_sanitized_printer = nil)
  if new_sanitized_printer
    @own_sanitized_printer = new_sanitized_printer
  else
    @own_sanitized_printer || GraphQL::Language::SanitizedPrinter
  end
end
static_validator() click to toggle source
# File lib/graphql/schema.rb, line 937
def static_validator
  GraphQL::StaticValidation::Validator.new(schema: self)
end
subscription(new_subscription_object = nil) click to toggle source
# File lib/graphql/schema.rb, line 1129
def subscription(new_subscription_object = nil)
  if new_subscription_object
    if @subscription_object
      raise GraphQL::Error, "Second definition of `subscription(...)` (#{new_subscription_object.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
    else
      @subscription_object = new_subscription_object
      add_subscription_extension_if_necessary
      add_type_and_traverse(new_subscription_object, root: true)
      nil
    end
  else
    @subscription_object || find_inherited_value(:subscription)
  end
end
subscription_execution_strategy(new_subscription_execution_strategy = nil) click to toggle source
# File lib/graphql/schema.rb, line 1328
def subscription_execution_strategy(new_subscription_execution_strategy = nil)
  if new_subscription_execution_strategy
    @subscription_execution_strategy = new_subscription_execution_strategy
  else
    @subscription_execution_strategy || find_inherited_value(:subscription_execution_strategy, self.default_execution_strategy)
  end
end
to_definition(only: nil, except: nil, context: {}) click to toggle source

Return the GraphQL IDL for the schema @param context [Hash] @param only [<#call(member, ctx)>] @param except [<#call(member, ctx)>] @return [String]

# File lib/graphql/schema.rb, line 885
def to_definition(only: nil, except: nil, context: {})
  GraphQL::Schema::Printer.print_schema(self, only: only, except: except, context: context)
end
to_document() click to toggle source

Return the GraphQL::Language::Document IDL AST for the schema @return [GraphQL::Language::Document]

# File lib/graphql/schema.rb, line 891
def to_document
  GraphQL::Language::DocumentFromSchemaDefinition.new(self).document
end
to_graphql() click to toggle source
# File lib/graphql/schema.rb, line 955
def to_graphql
  schema_defn = self.new
  schema_defn.raise_definition_error = true
  schema_defn.query = query && query.graphql_definition(silence_deprecation_warning: true)
  schema_defn.mutation = mutation && mutation.graphql_definition(silence_deprecation_warning: true)
  schema_defn.subscription = subscription && subscription.graphql_definition(silence_deprecation_warning: true)
  schema_defn.validate_timeout = validate_timeout
  schema_defn.validate_max_errors = validate_max_errors
  schema_defn.max_complexity = max_complexity
  schema_defn.error_bubbling = error_bubbling
  schema_defn.max_depth = max_depth
  schema_defn.default_max_page_size = default_max_page_size
  schema_defn.orphan_types = orphan_types.map { |t| t.graphql_definition(silence_deprecation_warning: true) }
  schema_defn.disable_introspection_entry_points = disable_introspection_entry_points?
  schema_defn.disable_schema_introspection_entry_point = disable_schema_introspection_entry_point?
  schema_defn.disable_type_introspection_entry_point = disable_type_introspection_entry_point?

  prepped_dirs = {}
  directives.each { |k, v| prepped_dirs[k] = v.graphql_definition}
  schema_defn.directives = prepped_dirs
  schema_defn.introspection_namespace = introspection
  schema_defn.resolve_type = method(:resolve_type)
  schema_defn.object_from_id = method(:object_from_id)
  schema_defn.id_from_object = method(:id_from_object)
  schema_defn.type_error = method(:type_error)
  schema_defn.context_class = context_class
  schema_defn.cursor_encoder = cursor_encoder
  schema_defn.tracers.concat(tracers)
  schema_defn.query_analyzers.concat(query_analyzers)
  schema_defn.analysis_engine = analysis_engine

  schema_defn.middleware.concat(all_middleware)
  schema_defn.multiplex_analyzers.concat(multiplex_analyzers)
  schema_defn.query_execution_strategy = query_execution_strategy
  schema_defn.mutation_execution_strategy = mutation_execution_strategy
  schema_defn.subscription_execution_strategy = subscription_execution_strategy
  schema_defn.default_mask = default_mask
  instrumenters.each do |step, insts|
    insts.each do |inst|
      schema_defn.instrumenters[step] << inst
    end
  end

  lazy_methods.each do |lazy_class, value_method|
    schema_defn.lazy_methods.set(lazy_class, value_method)
  end

  error_handler.each_rescue do |err_class, handler|
    schema_defn.rescue_from(err_class, &handler)
  end

  schema_defn.subscriptions ||= self.subscriptions

  if !schema_defn.interpreter?
    schema_defn.instrumenters[:query] << GraphQL::Schema::Member::Instrumentation
  end

  if new_connections?
    schema_defn.connections = self.connections
  end

  schema_defn.send(:rebuild_artifacts)

  schema_defn
end
to_json(**args) click to toggle source

Returns the JSON response of {Introspection::INTROSPECTION_QUERY}. @see {#as_json} @return [String]

# File lib/graphql/schema.rb, line 867
def to_json(**args)
  JSON.pretty_generate(as_json(**args))
end
tracer(new_tracer) click to toggle source
# File lib/graphql/schema.rb, line 1676
def tracer(new_tracer)
  own_tracers << new_tracer
end
tracers() click to toggle source
# File lib/graphql/schema.rb, line 1680
def tracers
  find_inherited_value(:tracers, EMPTY_ARRAY) + own_tracers
end
type_error(type_err, ctx) click to toggle source
# File lib/graphql/schema.rb, line 1614
def type_error(type_err, ctx)
  DefaultTypeError.call(type_err, ctx)
end
type_from_ast(ast_node, context: nil) click to toggle source
# File lib/graphql/schema.rb, line 1247
def type_from_ast(ast_node, context: nil)
  type_owner = context ? context.warden : self
  GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
end
types(context = GraphQL::Query::NullContext) click to toggle source

Build a map of ‘{ name => type }` and return it @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name @see get_type Which is more efficient for finding _one type_ by name, because it doesn’t merge hashes.

# File lib/graphql/schema.rb, line 1024
def types(context = GraphQL::Query::NullContext)
  all_types = non_introspection_types.merge(introspection_system.types)
  visible_types = {}
  all_types.each do |k, v|
    visible_types[k] =if v.is_a?(Array)
      visible_t = nil
      v.each do |t|
        if t.visible?(context)
          if visible_t.nil?
            visible_t = t
          else
            raise DuplicateNamesError, "Found two visible type definitions for `#{k}`: #{visible_t.inspect}, #{t.inspect}"
          end
        end
      end
      visible_t
    else
      v
    end
  end
  visible_types
end
unauthorized_field(unauthorized_error) click to toggle source

This hook is called when a field fails an ‘authorized?` check.

By default, this hook implements the same behavior as unauthorized_object.

Whatever value is returned from this method will be used instead of the unauthorized field . If an error is raised, then ‘nil` will be used.

If you want to add an error to the ‘“errors”` key, raise a {GraphQL::ExecutionError} in this hook.

@param unauthorized_error [GraphQL::UnauthorizedFieldError] @return [Field] The returned field will be put in the GraphQL response

# File lib/graphql/schema.rb, line 1610
def unauthorized_field(unauthorized_error)
  unauthorized_object(unauthorized_error)
end
unauthorized_object(unauthorized_error) click to toggle source

This hook is called when an object fails an ‘authorized?` check. You might report to your bug tracker here, so you can correct the field resolvers not to return unauthorized objects.

By default, this hook just replaces the unauthorized object with ‘nil`.

Whatever value is returned from this method will be used instead of the unauthorized object (accessible as ‘unauthorized_error.object`). If an error is raised, then `nil` will be used.

If you want to add an error to the ‘“errors”` key, raise a {GraphQL::ExecutionError} in this hook.

@param unauthorized_error [GraphQL::UnauthorizedError] @return [Object] The returned object will be put in the GraphQL response

# File lib/graphql/schema.rb, line 1594
def unauthorized_object(unauthorized_error)
  nil
end
union_memberships(type = nil) click to toggle source
# File lib/graphql/schema.rb, line 1197
def union_memberships(type = nil)
  if type
    own_um = own_union_memberships.fetch(type.graphql_name, EMPTY_ARRAY)
    inherited_um = find_inherited_value(:union_memberships, EMPTY_HASH).fetch(type.graphql_name, EMPTY_ARRAY)
    own_um + inherited_um
  else
    joined_um = own_union_memberships.dup
    find_inherited_value(:union_memberhips, EMPTY_HASH).each do |k, v|
      um = joined_um[k] ||= []
      um.concat(v)
    end
    joined_um
  end
end
use(plugin, **kwargs) click to toggle source
# File lib/graphql/schema.rb, line 941
def use(plugin, **kwargs)
  if kwargs.any?
    plugin.use(self, **kwargs)
  else
    plugin.use(self)
  end
  own_plugins << [plugin, kwargs]
end
using_ast_analysis?() click to toggle source
# File lib/graphql/schema.rb, line 1395
def using_ast_analysis?
  analysis_engine == GraphQL::Analysis::AST
end
validate(string_or_document, rules: nil, context: nil) click to toggle source

Validate a query string according to this schema. @param string_or_document [String, GraphQL::Language::Nodes::Document] @return [Array<GraphQL::StaticValidation::Error >]

# File lib/graphql/schema.rb, line 1351
def validate(string_or_document, rules: nil, context: nil)
  doc = if string_or_document.is_a?(String)
    GraphQL.parse(string_or_document)
  else
    string_or_document
  end
  query = GraphQL::Query.new(self, document: doc, context: context)
  validator_opts = { schema: self }
  rules && (validator_opts[:rules] = rules)
  validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
  res = validator.validate(query, timeout: validate_timeout, max_errors: validate_max_errors)
  res[:errors]
end
validate_max_errors(new_validate_max_errors = nil) click to toggle source
# File lib/graphql/schema.rb, line 1367
def validate_max_errors(new_validate_max_errors = nil)
  if new_validate_max_errors
    @validate_max_errors = new_validate_max_errors
  elsif defined?(@validate_max_errors)
    @validate_max_errors
  else
    find_inherited_value(:validate_max_errors)
  end
end
validate_timeout(new_validate_timeout = nil) click to toggle source
# File lib/graphql/schema.rb, line 1338
def validate_timeout(new_validate_timeout = nil)
  if new_validate_timeout
    @validate_timeout = new_validate_timeout
  elsif defined?(@validate_timeout)
    @validate_timeout
  else
    find_inherited_value(:validate_timeout)
  end
end
visible?(member, ctx) click to toggle source
# File lib/graphql/schema.rb, line 1559
def visible?(member, ctx)
  member.type_class.visible?(ctx)
end

Private Class Methods

add_type_and_traverse(t, root:) click to toggle source

@param t [Module, Array<Module>] @return [void]

# File lib/graphql/schema.rb, line 1800
def add_type_and_traverse(t, root:)
  if root
    @root_types ||= []
    @root_types << t
  end
  new_types = Array(t)
  addition = Schema::Addition.new(schema: self, own_types: own_types, new_types: new_types)
  addition.types.each do |name, types_entry| # rubocop:disable Development/ContextIsPassedCop -- build-time, not query-time
    if (prev_entry = own_types[name])
      prev_entries = case prev_entry
      when Array
        prev_entry
      when Module
        own_types[name] = [prev_entry]
      else
        raise "Invariant: unexpected prev_entry at #{name.inspect} when adding #{t.inspect}"
      end

      case types_entry
      when Array
        prev_entries.concat(types_entry)
        prev_entries.uniq! # in case any are being re-visited
      when Module
        if !prev_entries.include?(types_entry)
          prev_entries << types_entry
        end
      else
        raise "Invariant: unexpected types_entry at #{name} when adding #{t.inspect}"
      end
    else
      if types_entry.is_a?(Array)
        types_entry.uniq!
      end
      own_types[name] = types_entry
    end
  end

  own_possible_types.merge!(addition.possible_types) { |key, old_val, new_val| old_val + new_val }
  own_union_memberships.merge!(addition.union_memberships)

  addition.references.each { |thing, pointers|
    pointers.each { |pointer| references_to(thing, from: pointer) }
  }

  addition.directives.each { |dir_class| own_directives[dir_class.graphql_name] = dir_class }

  addition.arguments_with_default_values.each do |arg|
    arg.validate_default_value
  end
end
all_middleware() click to toggle source
# File lib/graphql/schema.rb, line 1905
def all_middleware
  find_inherited_value(:all_middleware, EMPTY_ARRAY) + own_middleware
end
lazy_methods() click to toggle source
# File lib/graphql/schema.rb, line 1851
def lazy_methods
  if !defined?(@lazy_methods)
    if inherited_map = find_inherited_value(:lazy_methods)
      # this isn't _completely_ inherited :S (Things added after `dup` won't work)
      @lazy_methods = inherited_map.dup
    else
      @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
      @lazy_methods.set(GraphQL::Execution::Lazy, :value)
      @lazy_methods.set(GraphQL::Dataloader::Request, :load)
    end
  end
  @lazy_methods
end
non_introspection_types() click to toggle source
# File lib/graphql/schema.rb, line 1869
def non_introspection_types
  find_inherited_value(:non_introspection_types, EMPTY_HASH).merge(own_types)
end
own_directives() click to toggle source
# File lib/graphql/schema.rb, line 1889
def own_directives
  @own_directives ||= {}
end
own_instrumenters() click to toggle source
# File lib/graphql/schema.rb, line 1893
def own_instrumenters
  @own_instrumenters ||= Hash.new { |h,k| h[k] = [] }
end
own_middleware() click to toggle source
# File lib/graphql/schema.rb, line 1909
def own_middleware
  @own_middleware ||= []
end
own_multiplex_analyzers() click to toggle source
# File lib/graphql/schema.rb, line 1913
def own_multiplex_analyzers
  @own_multiplex_analyzers ||= []
end
own_orphan_types() click to toggle source
# File lib/graphql/schema.rb, line 1877
def own_orphan_types
  @own_orphan_types ||= []
end
own_plugins() click to toggle source
# File lib/graphql/schema.rb, line 1873
def own_plugins
  @own_plugins ||= []
end
own_possible_types() click to toggle source
# File lib/graphql/schema.rb, line 1881
def own_possible_types
  @own_possible_types ||= {}
end
own_query_analyzers() click to toggle source
# File lib/graphql/schema.rb, line 1901
def own_query_analyzers
  @defined_query_analyzers ||= []
end
own_tracers() click to toggle source
# File lib/graphql/schema.rb, line 1897
def own_tracers
  @own_tracers ||= []
end
own_types() click to toggle source
# File lib/graphql/schema.rb, line 1865
def own_types
  @own_types ||= {}
end
own_union_memberships() click to toggle source
# File lib/graphql/schema.rb, line 1885
def own_union_memberships
  @own_union_memberships ||= {}
end

Public Instance Methods

accessible?(member, context) click to toggle source
# File lib/graphql/schema.rb, line 730
def accessible?(member, context)
  call_on_type_class(member, :accessible?, context, default: true)
end
as_json(only: nil, except: nil, context: {}) click to toggle source

Return the Hash response of {Introspection::INTROSPECTION_QUERY}. @param context [Hash] @param only [<#call(member, ctx)>] @param except [<#call(member, ctx)>] @return [Hash] GraphQL result

# File lib/graphql/schema.rb, line 825
def as_json(only: nil, except: nil, context: {})
  execute(Introspection.query(include_deprecated_args: true), only: only, except: except, context: context).to_h
end
call_on_type_class(member, method_name, context, default:) click to toggle source

Given this schema member, find the class-based definition object whose ‘method_name` should be treated as an application hook @see {.visible?} @see {.accessible?}

# File lib/graphql/schema.rb, line 708
def call_on_type_class(member, method_name, context, default:)
  member = if member.respond_to?(:type_class)
    member.type_class
  else
    member
  end

  if member.respond_to?(:relay_node_type) && (t = member.relay_node_type)
    member = t
  end

  if member.respond_to?(method_name)
    member.public_send(method_name, context)
  else
    default
  end
end
check_resolved_type(type, object, ctx = :__undefined__) { |type, object, ctx| ... } click to toggle source

This is a compatibility hack so that instance-level and class-level methods can get correctness checks without calling one another @api private

# File lib/graphql/schema.rb, line 604
def check_resolved_type(type, object, ctx = :__undefined__)
  if ctx == :__undefined__
    # Old method signature
    ctx = object
    object = type
    type = nil
  end

  if object.is_a?(GraphQL::Schema::Object)
    object = object.object
  end

  if type.respond_to?(:graphql_definition)
    type = type.graphql_definition
  end

  # Prefer a type-local function; fall back to the schema-level function
  type_proc = type && type.resolve_type_proc
  type_result = if type_proc
    type_proc.call(object, ctx)
  else
    yield(type, object, ctx)
  end

  if type_result.nil?
    nil
  else
    after_lazy(type_result) do |resolved_type_result|
      if resolved_type_result.respond_to?(:graphql_definition)
        resolved_type_result = resolved_type_result.graphql_definition
      end
      if !resolved_type_result.is_a?(GraphQL::BaseType)
        type_str = "#{resolved_type_result} (#{resolved_type_result.class.name})"
        raise "resolve_type(#{object}) returned #{type_str}, but it should return a GraphQL type"
      else
        resolved_type_result
      end
    end
  end
end
dataloader_class() click to toggle source
# File lib/graphql/schema.rb, line 1918
def dataloader_class
  self.class.dataloader_class
end
default_filter() click to toggle source
# File lib/graphql/schema.rb, line 257
def default_filter
  GraphQL::Filter.new(except: default_mask)
end
deprecated_define(**kwargs, &block) click to toggle source
# File lib/graphql/schema.rb, line 361
def deprecated_define(**kwargs, &block)
  super
  ensure_defined
  # Assert that all necessary configs are present:
  validation_error = Validation.validate(self)
  validation_error && raise(GraphQL::RequiredImplementationMissingError, validation_error)
  rebuild_artifacts

  @definition_error = nil
  nil
rescue StandardError => err
  if @raise_definition_error || err.is_a?(CyclicalDefinitionError) || err.is_a?(GraphQL::RequiredImplementationMissingError)
    raise
  else
    # Raise this error _later_ to avoid messing with Rails constant loading
    @definition_error = err
  end
  nil
end
disable_introspection_entry_points?() click to toggle source
# File lib/graphql/schema.rb, line 235
def disable_introspection_entry_points?
  !!@disable_introspection_entry_points
end
disable_schema_introspection_entry_point?() click to toggle source
# File lib/graphql/schema.rb, line 242
def disable_schema_introspection_entry_point?
  !!@disable_schema_introspection_entry_point
end
disable_type_introspection_entry_point?() click to toggle source
# File lib/graphql/schema.rb, line 249
def disable_type_introspection_entry_point?
  !!@disable_type_introspection_entry_point
end
execute(query_str = nil, **kwargs) click to toggle source

Execute a query on itself. Raises an error if the schema definition is invalid. @see {Query#initialize} for arguments. @return [Hash] query result, ready to be serialized as JSON

# File lib/graphql/schema.rb, line 444
def execute(query_str = nil, **kwargs)
  if query_str
    kwargs[:query] = query_str
  end
  # Some of the query context _should_ be passed to the multiplex, too
  multiplex_context = if (ctx = kwargs[:context])
    {
      backtrace: ctx[:backtrace],
      tracers: ctx[:tracers],
    }
  else
    {}
  end
  # Since we're running one query, don't run a multiplex-level complexity analyzer
  all_results = multiplex([kwargs], max_complexity: nil, context: multiplex_context)
  all_results[0]
end
execution_strategy_for_operation(operation) click to toggle source
# File lib/graphql/schema.rb, line 572
def execution_strategy_for_operation(operation)
  case operation
  when "query"
    query_execution_strategy
  when "mutation"
    mutation_execution_strategy
  when "subscription"
    subscription_execution_strategy
  else
    raise ArgumentError, "unknown operation type: #{operation}"
  end
end
find(path) click to toggle source

Search for a schema member using a string path @example Finding a Field Schema.find(“Ensemble.musicians”)

@see {GraphQL::Schema::Finder} for more examples @param path [String] A dot-separated path to the member @raise [Schema::Finder::MemberNotFoundError] if path could not be found @return [GraphQL::BaseType, GraphQL::Field, GraphQL::Argument, GraphQL::Directive] A GraphQL Schema Member

# File lib/graphql/schema.rb, line 494
def find(path)
  rebuild_artifacts unless defined?(@finder)
  @find_cache[path] ||= @finder.find(path)
end
get_field(parent_type, field_name) click to toggle source

Resolve field named ‘field_name` for type `parent_type`. Handles dynamic fields `__typename`, `__type` and `__schema`, too @param parent_type [String, GraphQL::BaseType] @param field_name [String] @return [GraphQL::Field, nil] The field named `field_name` on `parent_type` @see [GraphQL::Schema::Warden] Restricted access to members of a schema

# File lib/graphql/schema.rb, line 505
def get_field(parent_type, field_name)
  with_definition_error_check do
    parent_type_name = case parent_type
    when GraphQL::BaseType, Class, Module
      parent_type.graphql_name
    when String
      parent_type
    else
      raise "Unexpected parent_type: #{parent_type}"
    end

    defined_field = @instrumented_field_map[parent_type_name][field_name]
    if defined_field
      defined_field
    elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name))
      entry_point_field
    elsif (dynamic_field = introspection_system.dynamic_field(name: field_name))
      dynamic_field
    else
      nil
    end
  end
end
get_fields(type) click to toggle source

Fields for this type, after instrumentation is applied @return [Hash<String, GraphQL::Field>]

# File lib/graphql/schema.rb, line 531
def get_fields(type)
  @instrumented_field_map[type.graphql_name]
end
get_type(type_name) click to toggle source
# File lib/graphql/schema.rb, line 409
def get_type(type_name)
  @types[type_name]
end
id_from_object(object, type, ctx) click to toggle source

Get a unique identifier from this object @param object [Any] An application object @param type [GraphQL::BaseType] The current type definition @param ctx [GraphQL::Query::Context] the context for the current query @return [String] a unique identifier for ‘object` which clients can use to refetch it

# File lib/graphql/schema.rb, line 754
def id_from_object(object, type, ctx)
  if @id_from_object_proc.nil?
    raise(GraphQL::RequiredImplementationMissingError, "Can't generate an ID for #{object.inspect} of type #{type}, schema's `id_from_object` must be defined")
  else
    @id_from_object_proc.call(object, type, ctx)
  end
end
id_from_object=(new_proc) click to toggle source

@param new_proc [#call] A new callable for generating unique IDs

# File lib/graphql/schema.rb, line 763
def id_from_object=(new_proc)
  @id_from_object_proc = new_proc
end
initialize_copy(other) click to toggle source
# File lib/graphql/schema.rb, line 318
def initialize_copy(other)
  super
  @orphan_types = other.orphan_types.dup
  @directives = other.directives.dup
  @static_validator = GraphQL::StaticValidation::Validator.new(schema: self)
  @middleware = other.middleware.dup
  @query_analyzers = other.query_analyzers.dup
  @multiplex_analyzers = other.multiplex_analyzers.dup
  @tracers = other.tracers.dup
  @possible_types = GraphQL::Schema::PossibleTypes.new(self)

  @lazy_methods = other.lazy_methods.dup

  @instrumenters = Hash.new { |h, k| h[k] = [] }
  other.instrumenters.each do |key, insts|
    @instrumenters[key].concat(insts)
  end

  if other.rescues?
    @rescue_middleware = other.rescue_middleware
  end

  # This will be rebuilt when it's requested
  # or during a later `define` call
  @types = nil
  @introspection_system = nil
end
inspect() click to toggle source
# File lib/graphql/schema.rb, line 314
def inspect
  "#<#{self.class.name} ...>"
end
instrument(instrumentation_type, instrumenter) click to toggle source

Attach ‘instrumenter` to this schema for instrumenting events of `instrumentation_type`. @param instrumentation_type [Symbol] @param instrumenter @return [void]

# File lib/graphql/schema.rb, line 385
def instrument(instrumentation_type, instrumenter)
  @instrumenters[instrumentation_type] << instrumenter
  if instrumentation_type == :field
    rebuild_artifacts
  end
end
interpreter?() click to toggle source

@return [Boolean] True if using the new {GraphQL::Execution::Interpreter}

# File lib/graphql/schema.rb, line 308
def interpreter?
  query_execution_strategy == GraphQL::Execution::Interpreter &&
    mutation_execution_strategy == GraphQL::Execution::Interpreter &&
    subscription_execution_strategy == GraphQL::Execution::Interpreter
end
introspection_system() click to toggle source

@api private

# File lib/graphql/schema.rb, line 414
def introspection_system
  @introspection_system ||= begin
    rebuild_artifacts
    @introspection_system
  end
end
multiplex(queries, **kwargs) click to toggle source

Execute several queries on itself. Raises an error if the schema definition is invalid. @example Run several queries at once

context = { ... }
queries = [
  { query: params[:query_1], variables: params[:variables_1], context: context },
  { query: params[:query_2], variables: params[:variables_2], context: context },
]
results = MySchema.multiplex(queries)
render json: {
  result_1: results[0],
  result_2: results[1],
}

@see {Query#initialize} for query keyword arguments @see {Execution::Multiplex#run_queries} for multiplex keyword arguments @param queries [Array<Hash>] Keyword arguments for each query @param context [Hash] Multiplex-level context @return [Array<Hash>] One result for each query in the input

# File lib/graphql/schema.rb, line 480
def multiplex(queries, **kwargs)
  with_definition_error_check {
    GraphQL::Execution::Multiplex.run_all(self, queries, **kwargs)
  }
end
new_connections?() click to toggle source
# File lib/graphql/schema.rb, line 836
def new_connections?
  !!connections
end
object_from_id(id, ctx) click to toggle source

Fetch an application object by its unique id @param id [String] A unique identifier, provided previously by this GraphQL schema @param ctx [GraphQL::Query::Context] The context for the current query @return [Any] The application object identified by ‘id`

# File lib/graphql/schema.rb, line 654
def object_from_id(id, ctx)
  if @object_from_id_proc.nil?
    raise(GraphQL::RequiredImplementationMissingError, "Can't fetch an object for id \"#{id}\" because the schema's `object_from_id (id, ctx) -> { ... }` function is not defined")
  else
    @object_from_id_proc.call(id, ctx)
  end
end
object_from_id=(new_proc) click to toggle source

@param new_proc [#call] A new callable for fetching objects by ID

# File lib/graphql/schema.rb, line 663
def object_from_id=(new_proc)
  @object_from_id_proc = new_proc
end
parse_error(err, ctx) click to toggle source

A function to call when {#execute} receives an invalid query string

@see {DefaultParseError} is the default behavior. @param err [GraphQL::ParseError] The error encountered during parsing @param ctx [GraphQL::Query::Context] The context for the query where the error occurred @return void

# File lib/graphql/schema.rb, line 740
def parse_error(err, ctx)
  @parse_error_proc.call(err, ctx)
end
parse_error=(new_proc) click to toggle source

@param new_proc [#call] A new callable for handling parse errors during execution

# File lib/graphql/schema.rb, line 745
def parse_error=(new_proc)
  @parse_error_proc = new_proc
end
possible_types(type_defn, context = GraphQL::Query::NullContext) click to toggle source

@see [GraphQL::Schema::Warden] Restricted access to members of a schema @param type_defn [GraphQL::InterfaceType, GraphQL::UnionType] the type whose members you want to retrieve @param context [GraphQL::Query::Context] The context for the current query @return [Array<GraphQL::ObjectType>] types which belong to ‘type_defn` in this schema

# File lib/graphql/schema.rb, line 543
def possible_types(type_defn, context = GraphQL::Query::NullContext)
  if context == GraphQL::Query::NullContext
    @possible_types ||= GraphQL::Schema::PossibleTypes.new(self)
    @possible_types.possible_types(type_defn, context)
  else
    # Use the incoming context to cache this instance --
    # if it were cached on the schema, we'd have a memory leak
    # https://github.com/rmosolgo/graphql-ruby/issues/2878
    ns = context.namespace(:possible_types)
    per_query_possible_types = ns[:possible_types] ||= GraphQL::Schema::PossibleTypes.new(self)
    per_query_possible_types.possible_types(type_defn, context)
  end
end
references_to(type_name = nil) click to toggle source

Returns a list of Arguments and Fields referencing a certain type @param type_name [String] @return [Hash]

# File lib/graphql/schema.rb, line 424
def references_to(type_name = nil)
  rebuild_artifacts unless defined?(@type_reference_map)
  if type_name
    @type_reference_map.fetch(type_name, [])
  else
    @type_reference_map
  end
end
remove_handler(*args, &block) click to toggle source
# File lib/graphql/schema.rb, line 350
def remove_handler(*args, &block)
  rescue_middleware.remove_handler(*args, &block)
end
rescue_from(*args, &block) click to toggle source
# File lib/graphql/schema.rb, line 346
def rescue_from(*args, &block)
  rescue_middleware.rescue_from(*args, &block)
end
resolve_type(type, object, ctx = :__undefined__) click to toggle source

Determine the GraphQL type for a given object. This is required for unions and interfaces (including Relay’s ‘Node` interface) @see [GraphQL::Schema::Warden] Restricted access to members of a schema @param type [GraphQL::UnionType, GraphQL:InterfaceType] the abstract type which is being resolved @param object [Any] An application object which GraphQL is currently resolving on @param ctx [GraphQL::Query::Context] The context for the current query @return [GraphQL::ObjectType] The type for exposing `object` in GraphQL

# File lib/graphql/schema.rb, line 592
def resolve_type(type, object, ctx = :__undefined__)
  check_resolved_type(type, object, ctx) do |ok_type, ok_object, ok_ctx|
    if @resolve_type_proc.nil?
      raise(GraphQL::RequiredImplementationMissingError, "Can't determine GraphQL type for: #{ok_object.inspect}, define `resolve_type (type, obj, ctx) -> { ... }` inside `Schema.define`.")
    end
    @resolve_type_proc.call(ok_type, ok_object, ok_ctx)
  end
end
resolve_type=(new_resolve_type_proc) click to toggle source
# File lib/graphql/schema.rb, line 645
def resolve_type=(new_resolve_type_proc)
  callable = GraphQL::BackwardsCompatibility.wrap_arity(new_resolve_type_proc, from: 2, to: 3, last: true, name: "Schema#resolve_type(type, obj, ctx)")
  @resolve_type_proc = callable
end
root_type_for_operation(operation) click to toggle source

@see [GraphQL::Schema::Warden] Restricted access to root types @return [GraphQL::ObjectType, nil]

# File lib/graphql/schema.rb, line 559
def root_type_for_operation(operation)
  case operation
  when "query"
    query
  when "mutation"
    mutation
  when "subscription"
    subscription
  else
    raise ArgumentError, "unknown operation type: #{operation}"
  end
end
root_types() click to toggle source

@return [Array<GraphQL::BaseType>] The root types of this schema

# File lib/graphql/schema.rb, line 393
def root_types
  @root_types ||= begin
    rebuild_artifacts
    @root_types
  end
end
to_definition(only: nil, except: nil, context: {}) click to toggle source

Return the GraphQL IDL for the schema @param context [Hash] @param only [<#call(member, ctx)>] @param except [<#call(member, ctx)>] @return [String]

# File lib/graphql/schema.rb, line 807
def to_definition(only: nil, except: nil, context: {})
  GraphQL::Schema::Printer.print_schema(self, only: only, except: except, context: context)
end
to_document(only: nil, except: nil, context: {}) click to toggle source

Return the GraphQL::Language::Document IDL AST for the schema @param context [Hash] @param only [<#call(member, ctx)>] @param except [<#call(member, ctx)>] @return [GraphQL::Language::Document]

# File lib/graphql/schema.rb, line 816
def to_document(only: nil, except: nil, context: {})
  GraphQL::Language::DocumentFromSchemaDefinition.new(self, only: only, except: except, context: context).document
end
to_json(*args) click to toggle source

Returns the JSON response of {Introspection::INTROSPECTION_QUERY}. @see {#as_json} @return [String]

# File lib/graphql/schema.rb, line 832
def to_json(*args)
  JSON.pretty_generate(as_json(*args))
end
type_error(err, ctx) click to toggle source

When we encounter a type error during query execution, we call this hook.

You can use this hook to write a log entry, add a {GraphQL::ExecutionError} to the response (with ‘ctx.add_error`) or raise an exception and halt query execution.

@example A ‘nil` is encountered by a non-null field

type_error ->(err, query_ctx) {
  err.is_a?(GraphQL::InvalidNullError) # => true
}

@example An object doesn’t resolve to one of a {UnionType}‘s members

type_error ->(err, query_ctx) {
  err.is_a?(GraphQL::UnresolvedTypeError) # => true
}

@see {DefaultTypeError} is the default behavior. @param err [GraphQL::TypeError] The error encountered during execution @param ctx [GraphQL::Query::Context] The context for the field where the error occurred @return void

# File lib/graphql/schema.rb, line 687
def type_error(err, ctx)
  @type_error_proc.call(err, ctx)
end
type_error=(new_proc) click to toggle source

@param new_proc [#call] A new callable for handling type errors during execution

# File lib/graphql/schema.rb, line 692
def type_error=(new_proc)
  @type_error_proc = new_proc
end
type_from_ast(ast_node, context:) click to toggle source
# File lib/graphql/schema.rb, line 535
def type_from_ast(ast_node, context:)
  GraphQL::Schema::TypeExpression.build_type(self, ast_node)
end
types() click to toggle source

@see [GraphQL::Schema::Warden] Restricted access to members of a schema @return [GraphQL::Schema::TypeMap] ‘{ name => type }` pairs of types in this schema

# File lib/graphql/schema.rb, line 402
def types
  @types ||= begin
    rebuild_artifacts
    @types
  end
end
union_memberships(type) click to toggle source

Returns a list of Union types in which a type is a member @param type [GraphQL::ObjectType] @return [Array<GraphQL::UnionType>] list of union types of which the type is a member

# File lib/graphql/schema.rb, line 436
def union_memberships(type)
  rebuild_artifacts unless defined?(@union_memberships)
  @union_memberships.fetch(type.name, [])
end
using_ast_analysis?() click to toggle source
# File lib/graphql/schema.rb, line 354
def using_ast_analysis?
  @analysis_engine == GraphQL::Analysis::AST
end
visible?(member, context) click to toggle source
# File lib/graphql/schema.rb, line 726
def visible?(member, context)
  call_on_type_class(member, :visible?, context, default: true)
end

Protected Instance Methods

rescue_middleware() click to toggle source

Lazily create a middleware and add it to the schema (Don’t add it if it’s not used)

# File lib/graphql/schema.rb, line 1933
def rescue_middleware
  @rescue_middleware ||= GraphQL::Schema::RescueMiddleware.new.tap { |m| middleware.insert(0, m) }
end
rescues?() click to toggle source
# File lib/graphql/schema.rb, line 1927
def rescues?
  !!@rescue_middleware
end

Private Instance Methods

rebuild_artifacts() click to toggle source
# File lib/graphql/schema.rb, line 1939
def rebuild_artifacts
  if @rebuilding_artifacts
    raise CyclicalDefinitionError, "Part of the schema build process re-triggered the schema build process, causing an infinite loop. Avoid using Schema#types, Schema#possible_types, and Schema#get_field during schema build."
  else
    @rebuilding_artifacts = true
    @introspection_system = Schema::IntrospectionSystem.new(self)
    traversal = Traversal.new(self)
    @types = traversal.type_map
    @root_types = [query, mutation, subscription]
    @instrumented_field_map = traversal.instrumented_field_map
    @type_reference_map = traversal.type_reference_map
    @union_memberships = traversal.union_memberships
    @find_cache = {}
    @finder = Finder.new(self)
  end
ensure
  @rebuilding_artifacts = false
end
with_definition_error_check() { || ... } click to toggle source
# File lib/graphql/schema.rb, line 1961
def with_definition_error_check
  if @definition_error
    raise @definition_error
  else
    yield
  end
end