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, _context = GraphQL::Query::NullContext) 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, _context = GraphQL::Query::NullContext)
  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