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
- middleware for interacting with execution

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.

A schema accepts a `Relay::GlobalNodeIdentification` instance for use with Relay IDs.

@example defining a schema

MySchema = GraphQL::Schema.define do
  query QueryType
  middleware PermissionMiddleware
  rescue_from(ActiveRecord::RecordNotFound) { "Not found" }
  # 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
Context

Expose some query-specific info to field resolve functions. It delegates `[]` to the hash that's passed to `GraphQL::Query#initialize`.

DIRECTIVES
DYNAMIC_FIELDS

Attributes

default_execution_strategy[W]
ast_node[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]
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

Public Class Methods

accessible?(member, context) click to toggle source
# File lib/graphql/schema.rb, line 896
def accessible?(member, context)
  call_on_type_class(member, :accessible?, context, default: true)
end
context_class(new_context_class = nil) click to toggle source
# File lib/graphql/schema.rb, line 865
def context_class(new_context_class = nil)
  if new_context_class
    @context_class = new_context_class
  else
    @context_class || GraphQL::Query::Context
  end
end
cursor_encoder(new_encoder = nil) click to toggle source
# File lib/graphql/schema.rb, line 786
def cursor_encoder(new_encoder = nil)
  if new_encoder
    @cursor_encoder = new_encoder
  end
  @cursor_encoder || Base64Encoder
end
default_execution_strategy() click to toggle source
# File lib/graphql/schema.rb, line 857
def default_execution_strategy
  if superclass <= GraphQL::Schema
    superclass.default_execution_strategy
  else
    @default_execution_strategy
  end
end
default_max_page_size(new_default_max_page_size = nil) click to toggle source
# File lib/graphql/schema.rb, line 793
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
  end
end
directives(new_directives = nil) click to toggle source
# File lib/graphql/schema.rb, line 948
def directives(new_directives = nil)
  if new_directives
    @directives = new_directives.reduce({}) { |m, d| m[d.name] = d; m }
  end

  @directives ||= directives(DIRECTIVES)
end
error_bubbling(new_error_bubbling = nil) click to toggle source
# File lib/graphql/schema.rb, line 833
def error_bubbling(new_error_bubbling = nil)
  if !new_error_bubbling.nil?
    @error_bubbling = new_error_bubbling
  else
    @error_bubbling
  end
end
from_definition(definition_or_path, default_resolve: BuildFromDefinition::DefaultResolve, parser: BuildFromDefinition::DefaultParser) 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`) @return [GraphQL::Schema] the schema described by `document`

# File lib/graphql/schema.rb, line 597
def self.from_definition(definition_or_path, default_resolve: BuildFromDefinition::DefaultResolve, parser: BuildFromDefinition::DefaultParser)
  # If the file ends in `.graphql`, treat it like a filepath
  definition = if definition_or_path.end_with?(".graphql")
    File.read(definition_or_path)
  else
    definition_or_path
  end
  GraphQL::Schema::BuildFromDefinition.from_definition(definition, default_resolve: default_resolve, parser: parser)
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 588
def self.from_introspection(introspection_result)
  GraphQL::Schema::Loader.load(introspection_result)
end
graphql_definition() click to toggle source
# File lib/graphql/schema.rb, line 682
def graphql_definition
  @graphql_definition ||= to_graphql
end
id_from_object(object, type, ctx) click to toggle source
# File lib/graphql/schema.rb, line 888
def id_from_object(object, type, ctx)
  raise NotImplementedError, "#{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 908
def inaccessible_fields(error)
  error
end
inherited(child_class) click to toggle source
# File lib/graphql/schema.rb, line 1027
def self.inherited(child_class)
  child_class.singleton_class.class_eval do
    prepend(MethodWrappers)
  end
end
instrument(instrument_step, instrumenter, options = {}) click to toggle source
# File lib/graphql/schema.rb, line 939
def instrument(instrument_step, instrumenter, options = {})
  step = if instrument_step == :field && options[:after_built_ins]
    :field_after_built_ins
  else
    instrument_step
  end
  defined_instrumenters[step] << instrumenter
end
introspection(new_introspection_namespace = nil) click to toggle source
# File lib/graphql/schema.rb, line 778
def introspection(new_introspection_namespace = nil)
  if new_introspection_namespace
    @introspection = new_introspection_namespace
  else
    @introspection
  end
end
lazy_resolve(lazy_class, value_method) click to toggle source
# File lib/graphql/schema.rb, line 935
def lazy_resolve(lazy_class, value_method)
  lazy_classes[lazy_class] = value_method
end
max_complexity(max_complexity = nil) click to toggle source
# File lib/graphql/schema.rb, line 825
def max_complexity(max_complexity = nil)
  if max_complexity
    @max_complexity = max_complexity
  else
    @max_complexity
  end
end
max_depth(new_max_depth = nil) click to toggle source
# File lib/graphql/schema.rb, line 841
def max_depth(new_max_depth = nil)
  if new_max_depth
    @max_depth = new_max_depth
  else
    @max_depth
  end
end
middleware(new_middleware = nil) click to toggle source
# File lib/graphql/schema.rb, line 964
def middleware(new_middleware = nil)
  if new_middleware
    defined_middleware << new_middleware
  else
    graphql_definition.middleware
  end
end
multiplex_analyzer(new_analyzer) click to toggle source
# File lib/graphql/schema.rb, line 972
def multiplex_analyzer(new_analyzer)
  defined_multiplex_analyzers << new_analyzer
end
mutation(new_mutation_object = nil) click to toggle source
# File lib/graphql/schema.rb, line 762
def mutation(new_mutation_object = nil)
  if new_mutation_object
    @mutation_object = new_mutation_object
  else
    @mutation_object.respond_to?(:graphql_definition) ? @mutation_object.graphql_definition : @mutation_object
  end
end
mutation_execution_strategy(new_mutation_execution_strategy = nil) click to toggle source
# File lib/graphql/schema.rb, line 809
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 || self.default_execution_strategy
  end
end
new() click to toggle source
# File lib/graphql/schema.rb, line 148
def initialize
  @tracers = []
  @definition_error = nil
  @orphan_types = []
  @directives = DIRECTIVES.reduce({}) { |m, d| m[d.name] = d; m }
  @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
  # Default to the built-in execution strategy:
  @query_execution_strategy = self.class.default_execution_strategy
  @mutation_execution_strategy = self.class.default_execution_strategy
  @subscription_execution_strategy = self.class.default_execution_strategy
  @default_mask = GraphQL::Schema::NullMask
  @rebuilding_artifacts = false
  @context_class = GraphQL::Query::Context
  @introspection_namespace = nil
  @introspection_system = nil
  @error_bubbling = true
end
object_from_id(node_id, ctx) click to toggle source
# File lib/graphql/schema.rb, line 884
def object_from_id(node_id, ctx)
  raise NotImplementedError, "#{self.name}.object_from_id(node_id, ctx) must be implemented to use the `node` field (tried to load from id `#{node_id}`)"
end
orphan_types(*new_orphan_types) click to toggle source
# File lib/graphql/schema.rb, line 849
def orphan_types(*new_orphan_types)
  if new_orphan_types.any?
    @orphan_types = new_orphan_types.flatten
  else
    @orphan_types || []
  end
end
plugins() click to toggle source
# File lib/graphql/schema.rb, line 690
def plugins
  @plugins ||= []
end
query(new_query_object = nil) click to toggle source
# File lib/graphql/schema.rb, line 754
def query(new_query_object = nil)
  if new_query_object
    @query_object = new_query_object
  else
    @query_object.respond_to?(:graphql_definition) ? @query_object.graphql_definition : @query_object
  end
end
query_analyzer(new_analyzer) click to toggle source
# File lib/graphql/schema.rb, line 960
def query_analyzer(new_analyzer)
  defined_query_analyzers << new_analyzer
end
query_execution_strategy(new_query_execution_strategy = nil) click to toggle source
# File lib/graphql/schema.rb, line 801
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 || self.default_execution_strategy
  end
end
rescue_from(*err_classes, &handler_block) click to toggle source
# File lib/graphql/schema.rb, line 873
def rescue_from(*err_classes, &handler_block)
  @rescues ||= {}
  err_classes.each do |err_class|
    @rescues[err_class] = handler_block
  end
end
resolve_type(type, obj, ctx) click to toggle source
# File lib/graphql/schema.rb, line 880
def resolve_type(type, obj, ctx)
  raise NotImplementedError, "#{self.name}.resolve_type(type, obj, ctx) must be implemented to use Union types or Interface types (tried to resolve: #{type.name})"
end
subscription(new_subscription_object = nil) click to toggle source
# File lib/graphql/schema.rb, line 770
def subscription(new_subscription_object = nil)
  if new_subscription_object
    @subscription_object = new_subscription_object
  else
    @subscription_object.respond_to?(:graphql_definition) ? @subscription_object.graphql_definition : @subscription_object
  end
end
subscription_execution_strategy(new_subscription_execution_strategy = nil) click to toggle source
# File lib/graphql/schema.rb, line 817
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 || self.default_execution_strategy
  end
end
sync_lazy(value) { |value| ... } click to toggle source

Override this method to handle lazy objects in a custom way. @param value [Object] an instance of a class registered with {.lazy_resolve} @param ctx [GraphQL::Query::Context] the context for this query @return [Object] A GraphQL-ready (non-lazy) object

# File lib/graphql/schema.rb, line 1064
def self.sync_lazy(value)
  yield(value)
end
to_graphql() click to toggle source
# File lib/graphql/schema.rb, line 694
def to_graphql
  schema_defn = self.new
  schema_defn.raise_definition_error = true
  schema_defn.query = query
  schema_defn.mutation = mutation
  schema_defn.subscription = subscription
  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
  schema_defn.directives = directives
  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(defined_tracers)
  schema_defn.query_analyzers.concat(defined_query_analyzers)
  schema_defn.query_analyzers << GraphQL::Authorization::Analyzer
  schema_defn.middleware.concat(defined_middleware)
  schema_defn.multiplex_analyzers.concat(defined_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
  defined_instrumenters.each do |step, insts|
    insts.each do |inst|
      schema_defn.instrumenters[step] << inst
    end
  end
  schema_defn.instrumenters[:query] << GraphQL::Schema::Member::Instrumentation
  lazy_classes.each do |lazy_class, value_method|
    schema_defn.lazy_methods.set(lazy_class, value_method)
  end
  if @rescues
    @rescues.each do |err_class, handler|
      schema_defn.rescue_from(err_class, &handler)
    end
  end

  if plugins.any?
    schema_plugins = plugins
    # TODO don't depend on .define
    schema_defn = schema_defn.redefine do
      schema_plugins.each do |plugin, options|
        if options.any?
          use(plugin, **options)
        else
          use(plugin)
        end
      end
    end
  end
  schema_defn.send(:rebuild_artifacts)

  schema_defn
end
tracer(new_tracer) click to toggle source
# File lib/graphql/schema.rb, line 956
def tracer(new_tracer)
  defined_tracers << new_tracer
end
type_error(type_err, ctx) click to toggle source
# File lib/graphql/schema.rb, line 931
def type_error(type_err, ctx)
  DefaultTypeError.call(type_err, ctx)
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 ass `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 927
def unauthorized_object(unauthorized_error)
  nil
end
use(plugin, options = {}) click to toggle source
# File lib/graphql/schema.rb, line 686
def use(plugin, options = {})
  plugins << [plugin, options]
end
visible?(member, context) click to toggle source
# File lib/graphql/schema.rb, line 892
def visible?(member, context)
  call_on_type_class(member, :visible?, context, default: true)
end

Private Class Methods

call_on_type_class(member, method_name, *args, 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?} @see {.authorized?}

# File lib/graphql/schema.rb, line 1007
def call_on_type_class(member, method_name, *args, default:)
  member = if member.respond_to?(:metadata)
    member.metadata[:type_class] || member
  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, *args)
  else
    default
  end
end
defined_instrumenters() click to toggle source
# File lib/graphql/schema.rb, line 982
def defined_instrumenters
  @defined_instrumenters ||= Hash.new { |h,k| h[k] = [] }
end
defined_middleware() click to toggle source
# File lib/graphql/schema.rb, line 994
def defined_middleware
  @defined_middleware ||= []
end
defined_multiplex_analyzers() click to toggle source
# File lib/graphql/schema.rb, line 998
def defined_multiplex_analyzers
  @defined_multiplex_analyzers ||= []
end
defined_query_analyzers() click to toggle source
# File lib/graphql/schema.rb, line 990
def defined_query_analyzers
  @defined_query_analyzers ||= []
end
defined_tracers() click to toggle source
# File lib/graphql/schema.rb, line 986
def defined_tracers
  @defined_tracers ||= []
end
lazy_classes() click to toggle source
# File lib/graphql/schema.rb, line 978
def lazy_classes
  @lazy_classes ||= {}
end

Public Instance Methods

after_lazy(value) { |final_result| ... } click to toggle source

Call the given block at the right time, either:

  • Right away, if `value` is not registered with `lazy_resolve`

  • After resolving `value`, if it's registered with `lazy_resolve` (eg, `Promise`)

@api private

# File lib/graphql/schema.rb, line 1046
def after_lazy(value)
  if lazy?(value)
    GraphQL::Execution::Lazy.new do
      result = sync_lazy(value)
      # The returned result might also be lazy, so check it, too
      after_lazy(result) do |final_result|
        yield(final_result) if block_given?
      end
    end
  else
    yield(value) if block_given?
  end
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 640
def as_json(only: nil, except: nil, context: {})
  execute(Introspection::INTROSPECTION_QUERY, only: only, except: except, context: context).to_h
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 459
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.respond_to?(:graphql_definition)
    type_result = type_result.graphql_definition
  end

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

  @definition_error = nil
  nil
rescue StandardError => err
  if @raise_definition_error || err.is_a?(CyclicalDefinitionError)
    raise
  else
    # Raise this error _later_ to avoid messing with Rails constant loading
    @definition_error = err
  end
  nil
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 309
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 427
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 359
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 370
def get_field(parent_type, field_name)
  with_definition_error_check do
    parent_type_name = case parent_type
    when GraphQL::BaseType
      parent_type.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 396
def get_fields(type)
  @instrumented_field_map[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 572
def id_from_object(object, type, ctx)
  if @id_from_object_proc.nil?
    raise(NotImplementedError, "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 581
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 178
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
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 258
def instrument(instrumentation_type, instrumenter)
  @instrumenters[instrumentation_type] << instrumenter
  if instrumentation_type == :field
    rebuild_artifacts
  end
end
introspection_system() click to toggle source

@api private

# File lib/graphql/schema.rb, line 283
def introspection_system
  @introspection_system ||= begin
    rebuild_artifacts
    @introspection_system
  end
end
lazy?(obj) click to toggle source

@return [Boolean] True if this object should be lazily resolved

# File lib/graphql/schema.rb, line 616
def lazy?(obj)
  !!lazy_method_name(obj)
end
lazy_method_name(obj) click to toggle source

@return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered wtih {#lazy_resolve}.

# File lib/graphql/schema.rb, line 611
def lazy_method_name(obj)
  @lazy_methods.get(obj)
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 345
def multiplex(queries, **kwargs)
  with_definition_error_check {
    GraphQL::Execution::Multiplex.run_all(self, queries, **kwargs)
  }
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 506
def object_from_id(id, ctx)
  if @object_from_id_proc.nil?
    raise(NotImplementedError, "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 515
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 558
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 563
def parse_error=(new_proc)
  @parse_error_proc = new_proc
end
possible_types(type_defn) 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 @return [Array<GraphQL::ObjectType>] types which belong to `type_defn` in this schema

# File lib/graphql/schema.rb, line 407
def possible_types(type_defn)
  @possible_types ||= GraphQL::Schema::PossibleTypes.new(self)
  @possible_types.possible_types(type_defn)
end
references_to(type_name) 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 293
def references_to(type_name)
  rebuild_artifacts unless defined?(@type_reference_map)
  @type_reference_map.fetch(type_name, [])
end
remove_handler(*args, &block) click to toggle source
# File lib/graphql/schema.rb, line 210
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 206
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 447
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(NotImplementedError, "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 497
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] Resticted access to root types @return [GraphQL::ObjectType, nil]

# File lib/graphql/schema.rb, line 414
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 266
def root_types
  @root_types ||= begin
    rebuild_artifacts
    @root_types
  end
end
sync_lazy(value) click to toggle source

@see Schema.sync_lazy for a hook to override @api private

# File lib/graphql/schema.rb, line 1070
def sync_lazy(value)
  self.class.sync_lazy(value) { |v|
    lazy_method = lazy_method_name(v)
    if lazy_method
      synced_value = value.public_send(lazy_method)
      sync_lazy(synced_value)
    else
      v
    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 625
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 631
def to_document
  GraphQL::Language::DocumentFromSchemaDefinition.new(self).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 647
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 539
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 544
def type_error=(new_proc)
  @type_error_proc = new_proc
end
type_from_ast(ast_node) click to toggle source
# File lib/graphql/schema.rb, line 400
def type_from_ast(ast_node)
  GraphQL::Schema::TypeExpression.build_type(self.types, 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 275
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 301
def union_memberships(type)
  rebuild_artifacts unless defined?(@union_memberships)
  @union_memberships.fetch(type.name, [])
end
validate(string_or_document, rules: 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::Message>]

# File lib/graphql/schema.rb, line 220
def validate(string_or_document, rules: 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)
  validator_opts = { schema: self }
  rules && (validator_opts[:rules] = rules)
  validator = GraphQL::StaticValidation::Validator.new(validator_opts)
  res = validator.validate(query)
  res[:errors]
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 1090
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 1084
def rescues?
  !!@rescue_middleware
end

Private Instance Methods

rebuild_artifacts() click to toggle source
# File lib/graphql/schema.rb, line 1096
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 1118
def with_definition_error_check
  if @definition_error
    raise @definition_error
  else
    yield
  end
end