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
@see {GraphQL::Query::Context} The parent class of these classes @return [Class] Instantiated for each query
@return [<#call(member, ctx)>] A callable for filtering members of the schema @see {Query.new} for query-specific filters with `except:`
- Boolean
-
True if this object bubbles validation errors up from a field into its parent
InputObject
, if there is one.
@return [MiddlewareChain] MiddlewareChain
which is applied to fields during execution
Single, long-lived instance of the provided subscriptions class, if there is one. @return [GraphQL::Subscriptions]
@return [Array<#trace(key, data)>] Tracers applied to every query @see {Query#tracers} for query-specific tracers
Public Class Methods
# File lib/graphql/schema.rb, line 896 def accessible?(member, context) call_on_type_class(member, :accessible?, context, default: true) end
# 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
# 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
# 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
# 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
# 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
# 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
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
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
# File lib/graphql/schema.rb, line 682 def graphql_definition @graphql_definition ||= to_graphql end
# 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
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
# File lib/graphql/schema.rb, line 1027 def self.inherited(child_class) child_class.singleton_class.class_eval do prepend(MethodWrappers) end end
# 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
# 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
# File lib/graphql/schema.rb, line 935 def lazy_resolve(lazy_class, value_method) lazy_classes[lazy_class] = value_method end
# 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
# 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
# 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
# File lib/graphql/schema.rb, line 972 def multiplex_analyzer(new_analyzer) defined_multiplex_analyzers << new_analyzer end
# 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
# 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
# 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
# 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
# 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
# File lib/graphql/schema.rb, line 690 def plugins @plugins ||= [] end
# 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
# File lib/graphql/schema.rb, line 960 def query_analyzer(new_analyzer) defined_query_analyzers << new_analyzer end
# 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
# 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
# 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
# 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
# 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
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
# 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
# File lib/graphql/schema.rb, line 956 def tracer(new_tracer) defined_tracers << new_tracer end
# File lib/graphql/schema.rb, line 931 def type_error(type_err, ctx) DefaultTypeError.call(type_err, ctx) end
# File lib/graphql/schema.rb, line 686 def use(plugin, options = {}) plugins << [plugin, options] end
# File lib/graphql/schema.rb, line 892 def visible?(member, context) call_on_type_class(member, :visible?, context, default: true) end
Private Class Methods
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
# File lib/graphql/schema.rb, line 982 def defined_instrumenters @defined_instrumenters ||= Hash.new { |h,k| h[k] = [] } end
# File lib/graphql/schema.rb, line 994 def defined_middleware @defined_middleware ||= [] end
# File lib/graphql/schema.rb, line 998 def defined_multiplex_analyzers @defined_multiplex_analyzers ||= [] end
# File lib/graphql/schema.rb, line 990 def defined_query_analyzers @defined_query_analyzers ||= [] end
# File lib/graphql/schema.rb, line 986 def defined_tracers @defined_tracers ||= [] end
# File lib/graphql/schema.rb, line 978 def lazy_classes @lazy_classes ||= {} end
Public Instance Methods
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
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
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
# File lib/graphql/schema.rb, line 133 def default_filter GraphQL::Filter.new(except: default_mask) end
GraphQL::Define::InstanceDefinable#define
# 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 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
# 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
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
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
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
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
@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
GraphQL::Define::InstanceDefinable#initialize_copy
# 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
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
@api private
# File lib/graphql/schema.rb, line 283 def introspection_system @introspection_system ||= begin rebuild_artifacts @introspection_system end end
@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
@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
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
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
@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
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
@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
@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
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
# File lib/graphql/schema.rb, line 210 def remove_handler(*args, &block) rescue_middleware.remove_handler(*args, &block) end
# File lib/graphql/schema.rb, line 206 def rescue_from(*args, &block) rescue_middleware.rescue_from(*args, &block) end
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
# 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
@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
@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
@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
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
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
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
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
@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
# File lib/graphql/schema.rb, line 400 def type_from_ast(ast_node) GraphQL::Schema::TypeExpression.build_type(self.types, ast_node) end
@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
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 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
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
# File lib/graphql/schema.rb, line 1084 def rescues? !!@rescue_middleware end
Private Instance Methods
# 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
# File lib/graphql/schema.rb, line 1118 def with_definition_error_check if @definition_error raise @definition_error else yield end end