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)
Schema configuration reference
-
Root types are registered with
query,mutation, andsubscription; useorphan_typesfor interface-only object types. -
object_from_id,id_from_object, andresolve_typeimplementRelayobject identification and abstract-type resolution. -
type_error,rescue_from,parse_error, andquery_stack_errorprovide execution error hooks. -
max_depth,max_complexity,validate_timeout,validate_max_errors, andmax_query_string_tokenslimit incoming work. -
extra_types,introspection,trace_with, analyzers,context_class,query_class,lazy_resolve, anduseconfigure execution.
The schema definition guide contains setup examples and links each contract above to its API method. Keep method-specific behavior in the comments for those methods so this page remains the source of truth.
Root Types
query, mutation, and subscription register the entry-point object types for a schema. Each may receive a type class or a block for lazy loading:
query Types::Query mutation { Types::Mutation } subscription { Types::Subscription }
Use Schema.orphan_types for object types which implement an interface but arenβt reachable from a field return type. Use Schema.extra_types when a type should be printed and included in introspection without being connected to the schemaβs type graph.
Object Identification
Relay-style node(id:) fields, arguments configured with loads:, and the ObjectCache use Schema.object_from_id to fetch an application object. Return nil when the object does not exist or is not visible to the current operation. Implement Schema.id_from_object to produce a stable ID which can be passed back to object_from_id.
Schema.resolve_type maps an application object to its runtime GraphQL type when a field returns an interface or union.
Error Handling
Override Schema.type_error to handle mismatches between application values and the GraphQL type system. Register application exception handlers with Schema.rescue_from. Schema.parse_error handles invalid query strings, and Schema.query_stack_error is called when execution encounters a SystemStackError.
Default Limits
Schema.max_depth limits nested field selections and Schema.max_complexity limits the calculated cost of a query. Schema.default_max_page_size limits connection fields. Schema.validate_timeout, Schema.validate_max_errors, and Schema.max_query_string_tokens bound validation and parsing work. These limits can be configured on a schema and, where documented, overridden for an individual execution.
Introspection
Schema.extra_types adds otherwise unreachable types to printed SDL and introspection results. Pass a custom namespace to Schema.introspection to replace or extend the default introspection system.
Authorization
Schema.unauthorized_object and Schema.unauthorized_field run when an authorization hook returns false. Return a replacement value or raise GraphQL::ExecutionError to add a client-facing error.
Execution Configuration
Schema.trace_with installs tracing modules. Schema.query_analyzer and Schema.multiplex_analyzer register analysis hooks. Schema.default_logger configures runtime logging, while Schema.context_class and Schema.query_class select the classes used during execution. Schema.lazy_resolve registers promise-like values, and Schema.use installs schema plugins such as Dataloader and Visibility.
Examples
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
Constants
- BUILT_IN_TYPES
Attributes
Returns
-
GraphQL::Tracing::DetailedTraceβ if it has been configured for this schema
Public Class Methods
# File lib/graphql/schema.rb, line 2305 def allow_legacy_invalid_empty_selections_on_union(new_value = NOT_CONFIGURED) if NOT_CONFIGURED.equal?(new_value) if defined?(@allow_legacy_invalid_empty_selections_on_union) @allow_legacy_invalid_empty_selections_on_union else find_inherited_value(:allow_legacy_invalid_empty_selections_on_union) end else @allow_legacy_invalid_empty_selections_on_union = new_value end end
This setting controls how GraphQL-Ruby handles empty selections on Union types.
To opt into future, spec-compliant behavior where these selections are rejected, set this to false.
If you need to support previous, non-spec behavior which allowed selecting union fields but not selecting any fields on that union, set this to true to continue allowing that behavior.
If this is true, then .legacy_invalid_empty_selections_on_union_with_type will be called with Query objects with that kind of selections. You must implement that method
Parameters
-
new_value(Boolean)
Returns
-
true, false, nil
# File lib/graphql/schema.rb, line 2506 def allow_legacy_invalid_field_conflicts(new_value = NOT_CONFIGURED) if NOT_CONFIGURED.equal?(new_value) if defined?(@allow_legacy_invalid_field_conflicts) @allow_legacy_invalid_field_conflicts else find_inherited_value(:allow_legacy_invalid_field_conflicts) end else @allow_legacy_invalid_field_conflicts = new_value end end
This setting controls how GraphQL-Ruby handles fields with the same response name but different names or arguments, when previous versions of GraphQL-Ruby treated them as mutually exclusive, but the GraphQL spec doesnβt. The spec treats fields as mutually exclusive only when their parent types are different object types. For example:
-
... on Product { ...ProductFields options(first: 2) { id } }, whenProductFieldsalso selectsoptionsonProduct -
... on Feline { volume: meowVolume }and... on Canine { volume: barkVolume }, whenFelineandCanineare interfaces
When set to false, GraphQL-Ruby will reject those queries with a validation error (as per the GraphQL spec).
When set to true, GraphQL-Ruby will call .legacy_invalid_field_conflicts when the scenario is encountered.
When not configured, GraphQL-Ruby allows those queries and logs a warning.
Parameters
-
new_value(Boolean) βtruepermits the legacy behavior,falserejects it.
Returns
-
true, false, nil
# File lib/graphql/schema.rb, line 2443 def allow_legacy_invalid_return_type_conflicts(new_value = NOT_CONFIGURED) if NOT_CONFIGURED.equal?(new_value) if defined?(@allow_legacy_invalid_return_type_conflicts) @allow_legacy_invalid_return_type_conflicts else find_inherited_value(:allow_legacy_invalid_return_type_conflicts) end else @allow_legacy_invalid_return_type_conflicts = new_value end end
This setting controls how GraphQL-Ruby handles overlapping selections on scalar types when the types donβt match.
When set to false, GraphQL-Ruby will reject those queries with a validation error (as per the GraphQL spec).
When set to true, GraphQL-Ruby will call .legacy_invalid_return_type_conflicts when the scenario is encountered.
Parameters
-
new_value(Boolean) βtruepermits the legacy behavior,falserejects it.
Returns
-
true, false, nil
# File lib/graphql/schema.rb, line 2389 def allow_legacy_invalid_variable_usages(new_value = NOT_CONFIGURED) if NOT_CONFIGURED.equal?(new_value) if defined?(@allow_legacy_invalid_variable_usages) @allow_legacy_invalid_variable_usages else find_inherited_value(:allow_legacy_invalid_variable_usages) end else @allow_legacy_invalid_variable_usages = new_value end end
This setting controls how GraphQL-Ruby handles variable usages which the GraphQL spec doesnβt allow, but which previous versions of GraphQL-Ruby didnβt reject:
-
A variable in a fragment, when the fragment comes before the operation, or when another operation uses the fragment
-
A nullable variable with a
= nulldefault value, in a non-null position -
A variable in a nested list value, for example
[[$var]]
When set to false, GraphQL-Ruby will reject those queries with a validation error (as per the GraphQL spec).
When set to true, GraphQL-Ruby will call .legacy_invalid_variable_usages when the scenario is encountered.
When not configured, GraphQL-Ruby allows those queries and logs a warning.
Parameters
-
new_value(Boolean) βtruepermits the legacy behavior,falserejects it.
Returns
-
true, false, nil
Source
# File lib/graphql/schema.rb, line 1203 def analysis_engine @analysis_engine || find_inherited_value(:analysis_engine, self.default_analysis_engine) end
# File lib/graphql/schema.rb, line 425 def as_json(context: {}, include_deprecated_args: true, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false) introspection_query = Introspection.query( include_deprecated_args: include_deprecated_args, include_schema_description: include_schema_description, include_is_repeatable: include_is_repeatable, include_is_one_of: include_is_one_of, include_specified_by_url: include_specified_by_url, ) execute(introspection_query, context: context).to_h end
Return the Hash response of Introspection::INTROSPECTION_QUERY.
Parameters
-
context(Hash) -
include_deprecated_args(Boolean) β If true, deprecated arguments will be included in the JSON response -
include_schema_description(Boolean) β If true, the schemaβs description will be queried and included in the response -
include_is_repeatable(Boolean) β If true,isRepeatable: true|falsewill be included with the schemaβs directives -
include_specified_by_url(Boolean) β If true, scalar typesβspecifiedByUrl:will be included in the response -
include_is_one_of(Boolean) β If true,isOneOf: true|falsewill be included with input objects
Returns
-
HashβGraphQLresult
Source
# File lib/graphql/schema.rb, line 348 def build_trace_mode(mode) case mode when :default # Use the superclass's default mode if it has one, or else start an inheritance chain at the built-in base class. base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode, build: true)) || GraphQL::Tracing::Trace const_set(:DefaultTrace, Class.new(base_class) do include DefaultTraceClass end) else # First, see if the superclass has a custom-defined class for this. # Then, if it doesn't, use this class's default trace base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || trace_class_for(:default, build: true) # Prepare the default trace class if it hasn't been initialized yet base_class ||= (own_trace_modes[:default] = build_trace_mode(:default)) mods = trace_modules_for(mode) if base_class < DefaultTraceClass mods = trace_modules_for(:default) + mods end # Copy the existing default options into this mode's options default_options = trace_options_for(:default) add_trace_options_for(mode, default_options) Class.new(base_class) do !mods.empty? && include(*mods) end end end
# File lib/graphql/schema.rb, line 2574 def complexity_cost_calculation_mode(new_mode = NOT_CONFIGURED) if NOT_CONFIGURED.equal?(new_mode) if defined?(@complexity_cost_calculation_mode) @complexity_cost_calculation_mode else find_inherited_value(:complexity_cost_calculation_mode) end else @complexity_cost_calculation_mode = new_mode end end
The legacy complexity implementation included several bugs:
-
In some cases, it used the lexically last field to determine a cost, instead of calculating the maximum among selections
-
In some cases, it called field complexity hooks repeatedly (when it should have only called them once)
The future implementation may produce higher total complexity scores, so itβs not active by default yet. You can opt into the future default behavior by configuring :future here. Or, you can choose a mode for each query with .complexity_cost_calculation_mode_for.
The legacy mode is currently maintained alongside the future one, but it will be removed in a future GraphQL-Ruby version.
If you choose :compare, you must also implement .legacy_complexity_cost_calculation_mismatch to handle the input somehow.
Examples
Example: Opting into the future calculation mode
complexity_cost_calculation_mode(:future)
Example: Choosing the legacy mode (which will work until that mode is removed...)
complexity_cost_calculation_mode(:legacy)
Example: Run both modes for every query, call .legacy_complexity_cost_calculation_mismatch when they donβt match:
complexity_cost_calculation_mode(:compare)
# File lib/graphql/schema.rb, line 2632 def complexity_cost_calculation_mode_for(multiplex_context) complexity_cost_calculation_mode end
Implement this method to produce a per-query complexity cost calculation mode. (Technically, itβs per-multiplex.)
This is a way to check the compatibility of queries coming to your API without adding overhead of running :compare for every query. You could sample traffic, turn it off/on with feature flags, or anything else.
Examples
Example: Sampling traffic
def self.complexity_cost_calculation_mode_for(_context) if rand < 0.1 # 10% of the time :compare else :legacy end end
Example: Using a feature flag to manage future mode
def complexity_cost_calculation_mode_for(context) current_user = context[:current_user] if Flipper.enabled?(:future_complexity_cost, current_user) :future elsif rand < 0.5 # 50% :compare else :legacy end end
Parameters
-
multiplex_context(Hash) β The context for the currently-runningExecution::Multiplex(which contains one or more queries)
Returns
-
:futureβ Use the new calculation algorithm β may be higher than:legacy -
:legacyβ Use the legacy calculation algorithm, warts and all -
:compareβ Run both algorithms and call.legacy_complexity_cost_calculation_mismatchif they donβt match
# File lib/graphql/schema.rb, line 631 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
Returns
-
GraphQL::Pagination::Connectionsβ if installed
# File lib/graphql/schema.rb, line 1424 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
Parameters
-
new_context_class(Class<GraphQL::Query::Context>) β A subclass to use when executing queries
# File lib/graphql/schema.rb, line 1231 def count_introspection_fields if defined?(@count_introspection_fields) @count_introspection_fields else find_inherited_value(:count_introspection_fields, true) end end
# File lib/graphql/schema.rb, line 1042 def cursor_encoder(new_encoder = nil) if new_encoder @cursor_encoder = new_encoder end @cursor_encoder || find_inherited_value(:cursor_encoder, Base64Encoder) end
Source
# File lib/graphql/schema.rb, line 1363 def default_analysis_engine if superclass <= GraphQL::Schema superclass.default_analysis_engine else @default_analysis_engine ||= GraphQL::Analysis::AST end end
Source
# File lib/graphql/schema.rb, line 1836 def default_directives @default_directives ||= { "include" => GraphQL::Schema::Directive::Include, "skip" => GraphQL::Schema::Directive::Skip, "deprecated" => GraphQL::Schema::Directive::Deprecated, "oneOf" => GraphQL::Schema::Directive::OneOf, "specifiedBy" => GraphQL::Schema::Directive::SpecifiedBy, }.freeze end
# File lib/graphql/schema.rb, line 2151 def default_execution_next(new_value = NOT_CONFIGURED) if !NOT_CONFIGURED.equal?(new_value) @default_execution_next = new_value elsif instance_variable_defined?(:@default_execution_next) @default_execution_next elsif superclass.respond_to?(:default_execution_next) superclass.default_execution_next else false end end
# File lib/graphql/schema.rb, line 1355 def default_execution_strategy if superclass <= GraphQL::Schema superclass.default_execution_strategy else @default_execution_strategy ||= GraphQL::Execution::Interpreter end end
# File lib/graphql/schema.rb, line 1378 def default_logger(new_default_logger = NOT_CONFIGURED) if NOT_CONFIGURED.equal?(new_default_logger) if defined?(@default_logger) @default_logger elsif superclass.respond_to?(:default_logger) superclass.default_logger elsif defined?(Rails) && Rails.respond_to?(:logger) && (rails_logger = Rails.logger) rails_logger else def_logger = Logger.new($stdout) def_logger.info! # It doesn't output debug info by default def_logger end elsif new_default_logger == nil @default_logger = Logger.new(IO::NULL) else @default_logger = new_default_logger end end
Parameters
-
new_default_logger(#log) β Something to use for logging messages
# File lib/graphql/schema.rb, line 1049 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
# File lib/graphql/schema.rb, line 1074 def default_page_size(new_default_page_size = nil) if new_default_page_size @default_page_size = new_default_page_size else @default_page_size || find_inherited_value(:default_page_size) end end
# File lib/graphql/schema.rb, line 275 def default_trace_mode(new_mode = NOT_CONFIGURED) if !NOT_CONFIGURED.equal?(new_mode) @default_trace_mode = new_mode elsif defined?(@default_trace_mode) && !@default_trace_mode.nil? # This `nil?` check seems necessary because of # Ractors silently initializing @default_trace_mode somehow @default_trace_mode elsif superclass.respond_to?(:default_trace_mode) superclass.default_trace_mode else :default end end
Parameters
-
new_mode(Symbol) β If configured, this will be used whencontext: { trace_mode: ... }isnβt set.
# File lib/graphql/schema.rb, line 251 def deprecated_graphql_definition graphql_definition(silence_deprecation_warning: true) end
# File lib/graphql/schema.rb, line 471 def description(new_description = nil) if new_description @description = new_description elsif defined?(@description) @description else find_inherited_value(:description, nil) end end
Returns
-
String, nil
# File lib/graphql/schema.rb, line 1863 def detailed_trace?(query) raise "#{self} must implement `def.detailed_trace?(query)` to use DetailedTrace. Implement this method in your schema definition." end
Tracing::DetailedTrace saves traces when this method returns true.
Parameters
-
query(GraphQL::Query, GraphQL::Execution::Multiplex) β Called with a multiplex when multiple queries are executed at once (with.multiplex)
Returns
-
Booleanβ Whentrue, save a detailed trace for this query.
# File lib/graphql/schema.rb, line 2272 def did_you_mean(new_dym = NOT_CONFIGURED) if NOT_CONFIGURED.equal?(new_dym) if defined?(@did_you_mean) @did_you_mean else find_inherited_value(:did_you_mean, defined?(DidYouMean) ? DidYouMean : nil) end else @did_you_mean = new_dym end end
Returns DidYouMean if itβs defined. Override this to return nil if you donβt want to use DidYouMean
# File lib/graphql/schema.rb, line 1828 def directive(new_directive) if use_visibility_profile? own_directives[new_directive.graphql_name] = new_directive else add_type_and_traverse(new_directive, root: false) end end
# File lib/graphql/schema.rb, line 1803 def directives(*new_directives) if !new_directives.empty? new_directives.flatten.each { |d| directive(d) } end inherited_dirs = find_inherited_value(:directives, default_directives) if !own_directives.empty? inherited_dirs.merge(own_directives) else inherited_dirs end end
Add several directives at once
Parameters
-
new_directives(Class)
# File lib/graphql/schema.rb, line 1239 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
# File lib/graphql/schema.rb, line 1257 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
# File lib/graphql/schema.rb, line 1245 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
# File lib/graphql/schema.rb, line 1265 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
# File lib/graphql/schema.rb, line 1251 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
# File lib/graphql/schema.rb, line 1273 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
# File lib/graphql/schema.rb, line 1207 def error_bubbling(new_error_bubbling = nil) if !new_error_bubbling.nil? warn("error_bubbling(#{new_error_bubbling.inspect}) is deprecated; the default value of `false` will be the only option in GraphQL-Ruby 3.0") @error_bubbling = new_error_bubbling else @error_bubbling.nil? ? find_inherited_value(:error_bubbling) : @error_bubbling end end
Source
# File lib/graphql/schema.rb, line 1468 def error_handlers @error_handlers ||= begin new_handler_hash = ->(h, k) { h[k] = { class: k, handler: nil, subclass_handlers: Hash.new(&new_handler_hash), } } { class: nil, handler: nil, subclass_handlers: Hash.new(&new_handler_hash), } end end
# File lib/graphql/schema.rb, line 2076 def execute(query_str = nil, **kwargs) if default_execution_next execute_next(query_str, **kwargs) else execute_legacy(query_str, **kwargs) end end
Execute a query on itself. See the GraphQL::Query constructor for arguments.
query_str may be a query string; alternatively pass document: with a parsed document. The common options are variables:, context:, root_value:, operation_name:, validate:, max_depth:, and max_complexity:. The returned result can be serialized directly as JSON.
Returns
-
GraphQL::Query::Resultβ query result, ready to be serialized as JSON
# File lib/graphql/schema.rb, line 2084 def execute_legacy(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], trace: ctx[:trace], dataloader: ctx[:dataloader], trace_mode: ctx[:trace_mode], } 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 1291 def extra_types(*new_extra_types) if !new_extra_types.empty? new_extra_types = new_extra_types.flatten @own_extra_types ||= [] @own_extra_types.concat(new_extra_types) end inherited_et = find_inherited_value(:extra_types, nil) if inherited_et if @own_extra_types inherited_et + @own_extra_types else inherited_et end else @own_extra_types || EMPTY_ARRAY end end
Parameters
-
new_extra_types(Module) β Type definitions to include in printing and introspection, even though they arenβt referenced in the schema
Returns
-
Array<Module>β Type definitions added to this schema
Source
# File lib/graphql/schema.rb, line 481 def find(path) if !@finder @find_cache = {} @finder ||= GraphQL::Schema::Finder.new(self) end @find_cache[path] ||= @finder.find(path) end
# File lib/graphql/schema.rb, line 228 def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {}, base_types: {}) # If the file ends in `.graphql` or `.graphqls`, treat it like a filepath if definition_or_path.end_with?(".graphql") || definition_or_path.end_with?(".graphqls") GraphQL::Schema::BuildFromDefinition.from_definition_path( self, definition_or_path, default_resolve: default_resolve, parser: parser, using: using, base_types: base_types, ) else GraphQL::Schema::BuildFromDefinition.from_definition( self, definition_or_path, default_resolve: default_resolve, parser: parser, using: using, base_types: base_types, ) end end
Create schema from an IDL schema or file containing an IDL definition.
Parameters
-
definition_or_path(String) β A schema definition string, or a path to a file containing the definition -
default_resolve(<#call(type, field, obj, args, ctx)>) β A callable for handling field resolution -
parser(Object) β An object for handling definition string parsing (must respond toparse) -
using(Hash) β Plugins to attach to the created schema withuse(key, value)
Returns
-
Classβ the schema described bydocument
# File lib/graphql/schema.rb, line 209 def from_introspection(introspection_result) GraphQL::Schema::Loader.load(introspection_result) end
Create schema with the result of an introspection query.
Parameters
-
introspection_result(Hash) β A response fromGraphQL::Introspection::INTROSPECTION_QUERY
Returns
-
Class<GraphQL::Schema>β the schema described byinput
# File lib/graphql/schema.rb, line 962 def get_field(type_or_name, field_name, context = null_context, use_visibility_profile = use_visibility_profile?) if use_visibility_profile profile = Visibility::Profile.from_context(context, self) parent_type = case type_or_name when String profile.type(type_or_name) when Module type_or_name when LateBoundType profile.type(type_or_name.name) else raise GraphQL::InvariantError, "Unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})" end return profile.field(parent_type, field_name) end 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
# File lib/graphql/schema.rb, line 999 def get_fields(type, context = null_context) type.fields(context) end
Source
# File lib/graphql/schema.rb, line 574 def get_type(type_name, context = null_context, use_visibility_profile = use_visibility_profile?) if use_visibility_profile profile = Visibility::Profile.from_context(context, self) return profile.type(type_name) end local_entry = own_types[type_name] type_defn = case local_entry when nil nil when Array if context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile) local_entry else 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.new( duplicated_name: type_name, duplicated_definition_1: visible_t.inspect, duplicated_definition_2: t.inspect ) end end end visible_t end 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, use_visibility_profile) : nil) end
Parameters
-
type_name(String) -
context(GraphQL::Query::Context) β Used for filtering definitions at query-time -
use_visibility_profileβ Private, for migration toSchema::Visibility
Returns
-
Module, nilβ A type, or nil if thereβs no type calledtype_name
# File lib/graphql/schema.rb, line 619 def has_defined_type?(type_name) own_types.key?(type_name) || introspection_system.types.key?(type_name) || (superclass.respond_to?(:has_defined_type?) ? superclass.has_defined_type?(type_name) : false) end
Returns
-
Booleanβ Does this schema have any definition for a type namedtype_name, regardless of visibility?
# File lib/graphql/schema.rb, line 1643 def id_from_object(application_object, graphql_type, context) raise GraphQL::RequiredImplementationMissingError, "#{self.name}.id_from_object(application_object, graphql_type, context) must be implemented to create global ids (tried to create an id for `#{application_object.inspect}`)" end
Return a stable ID string for object so that it can be refetched later, using .object_from_id.
GlobalID and SQIDs can both be used to create IDs.
Examples
Example: Using Railsβs GlobalID to generate IDs
def self.id_from_object(application_object, graphql_type, context) application_object.to_gid_param end
Parameters
-
application_object(Object) β Some object encountered by GraphQL-Ruby while running a query -
graphql_type(Class, Module) β The type that GraphQL-Ruby is using forapplication_objectduring this query -
context(GraphQL::Query::Context) β The context for the operation that is currently running
Returns
-
Stringβ A stable identifier which can be passed to.object_from_idlater to re-fetchapplication_object
Source
# File lib/graphql/schema.rb, line 1566 def inherited(child_class) if self == GraphQL::Schema child_class.directives(default_directives.values) child_class.extend(SubclassGetReferencesTo) end # Make sure the child class has these built out, so that # subclasses can be modified by later calls to `trace_with` own_trace_modes.each do |name, _class| child_class.own_trace_modes[name] = child_class.build_trace_mode(name) end child_class.singleton_class.prepend(ResolveTypeWithType) if use_visibility_profile? vis = self.visibility child_class.visibility = vis.dup_for(child_class) end child_class.null_context = Query::NullContext.new(schema: child_class) super end
rubocop:enable Lint/DuplicateMethods
# File lib/graphql/schema.rb, line 1785 def instrument(instrument_step, instrumenter, options = {}) warn <<~WARN Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html" (From `#{self}.instrument(#{instrument_step}, #{instrumenter})` at #{caller(1, 1).first}) WARN trace_with(Tracing::LegacyHooksTrace) own_instrumenters[instrument_step] << instrumenter end
Source
# File lib/graphql/schema.rb, line 2163 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
# File lib/graphql/schema.rb, line 1015 def introspection(new_introspection_namespace = nil) if new_introspection_namespace @introspection = new_introspection_namespace # reset this cached value: @introspection_system = nil introspection_system self.visibility&.introspection_system_configured(introspection_system) @introspection else @introspection || find_inherited_value(:introspection) end end
Pass a custom introspection module here to use it for this schema.
Parameters
-
new_introspection_namespace(Module) β If given, use this module for custom introspection on the schema
Returns
-
Module, nilβ The configured namespace, if there is one
# File lib/graphql/schema.rb, line 1034 def introspection_system if !@introspection_system @introspection_system = Schema::IntrospectionSystem.new(self) @introspection_system.resolve_late_bindings end @introspection_system end
Returns
-
Schema::IntrospectionSystemβ Based onintrospection
Source
# File lib/graphql/schema.rb, line 2250 def lazy?(obj) !!lazy_method_name(obj) end
Returns
-
Booleanβ True if this object should be lazily resolved
# File lib/graphql/schema.rb, line 2240 def lazy_method_name(obj) lazy_methods.get(obj) end
Returns
-
Symbol, nilβ The method name to lazily resolveobj, or nil ifobjβs class wasnβt registered with.lazy_resolve.
# File lib/graphql/schema.rb, line 1771 def lazy_resolve(lazy_class, value_method) lazy_methods.set(lazy_class, value_method) end
# File lib/graphql/schema.rb, line 2666 def legacy_complexity_cost_calculation_mismatch(multiplex, future_complexity_cost, legacy_complexity_cost) raise "Implement #{self}.legacy_complexity_cost(multiplex, future_complexity_cost, legacy_complexity_cost) to handle this mismatch (#{future_complexity_cost} vs. #{legacy_complexity_cost}) and return a value to use" end
Implement this method in your schema to handle mismatches when :compare is used.
See Query::Context#add_error Adding an error to the response to notify the client See Query::Context#response_extensions Adding key-value pairs to the response "extensions" => { ... }
Examples
Example: Logging the mismatch
def self.legacy_cost_calculation_mismatch(multiplex, future_cost, legacy_cost) client_id = multiplex.context[:api_client].id operation_names = multiplex.queries.map { |q| q.selected_operation_name || "anonymous" }.join(", ") Stats.increment(:complexity_mismatch, tags: { client: client_id, ops: operation_names }) legacy_cost end
Parameters
-
multiplex(GraphQL::Execution::Multiplex) -
future_complexity_cost(Integer) -
legacy_complexity_cost(Integer)
Returns
-
Integerβ the cost to use for this query (probably one offuture_complexity_costorlegacy_complexity_cost)
# File lib/graphql/schema.rb, line 2338 def legacy_invalid_empty_selections_on_union(query) raise "Implement `def self.legacy_invalid_empty_selections_on_union_with_type(query, type)` or `def self.legacy_invalid_empty_selections_on_union(query)` to handle this scenario" end
This method is called during validation when a previously-allowed, but non-spec query is encountered where a union field has no child selections on it.
If legacy_invalid_empty_selections_on_union_with_type is overridden, this method will not be called.
You should implement this method or legacy_invalid_empty_selections_on_union_with_type to log the violation so that you can contact clients and notify them about changing their queries. Then return a suitable value to tell GraphQL-Ruby how to continue.
Parameters
-
query(GraphQL::Query)
Returns
-
:return_validation_errorβ Let GraphQL-Ruby return the (new) normal validation error for this query -
Stringβ A validation error to return for this query -
nilβ Donβt send the client an error, continue the legacy behavior (allow this query to execute)
# File lib/graphql/schema.rb, line 2362 def legacy_invalid_empty_selections_on_union_with_type(query, type) legacy_invalid_empty_selections_on_union(query) end
This method is called during validation when a previously-allowed, but non-spec query is encountered where a union field has no child selections on it.
You should implement this method to log the violation so that you can contact clients and notify them about changing their queries. Then return a suitable value to tell GraphQL-Ruby how to continue.
Parameters
-
query(GraphQL::Query) -
type(Module) β AGraphQLtype definition
Returns
-
:return_validation_errorβ Let GraphQL-Ruby return the (new) normal validation error for this query -
Stringβ A validation error to return for this query -
nilβ Donβt send the client an error, continue the legacy behavior (allow this query to execute)
# File lib/graphql/schema.rb, line 2539 def legacy_invalid_field_conflicts(query, node1, node2) raise "Implement #{self}.legacy_invalid_field_conflicts to handle this invalid selection" end
This method is called when the query has two fields with the same response name but different names or arguments, which previous versions of GraphQL-Ruby allowed. See .allow_legacy_invalid_field_conflicts.
You should implement this method to log the violation so that you can contact clients and notify them about changing their queries. Then return a suitable value to tell GraphQL-Ruby how to continue.
Parameters
-
query(GraphQL::Query) -
node1(GraphQL::Language::Nodes::Field) β This node is recognized as conflicting. You might call.lineand.colfor custom error reporting. -
node2(GraphQL::Language::Nodes::Field) β The other node recognized as conflicting.
Returns
-
:return_validation_errorβ Let GraphQL-Ruby return the (new) normal validation error for this query -
Stringβ A validation error to return for this query -
nilβ Donβt send the client an error, continue the legacy behavior (allow this query to execute)
# File lib/graphql/schema.rb, line 2479 def legacy_invalid_return_type_conflicts(query, type1, type2, node1, node2) raise "Implement #{self}.legacy_invalid_return_type_conflicts to handle this invalid selection" end
This method is called when the query contains fields which donβt contain matching scalar types. This was previously allowed by GraphQL-Ruby but itβs a violation of the GraphQL spec.
You should implement this method to log the violation so that you observe usage of these fields. Fixing this scenario might mean adding new fields, and telling clients to use those fields. (Changing the field return type would be a breaking change, but if it works for your client use cases, that might work, too.)
Parameters
-
query(GraphQL::Query) -
type1(Module) β AGraphQLtype definition -
type2(Module) β AGraphQLtype definition -
node1(GraphQL::Language::Nodes::Field) β This node is recognized as conflicting. You might call.lineand.colfor custom error reporting. -
node2(GraphQL::Language::Nodes::Field) β The other node recognized as conflicting.
Returns
-
:return_validation_errorβ Let GraphQL-Ruby return the (new) normal validation error for this query -
Stringβ A validation error to return for this query -
nilβ Donβt send the client an error, continue the legacy behavior (allow this query to execute)
# File lib/graphql/schema.rb, line 2422 def legacy_invalid_variable_usages(query, variable_definition, argument) raise "Implement #{self}.legacy_invalid_variable_usages to handle this invalid variable usage" end
This method is called when the query uses a variable in a way which the GraphQL spec doesnβt allow, but which previous versions of GraphQL-Ruby allowed. See .allow_legacy_invalid_variable_usages.
You should implement this method to log the violation so that you can contact clients and notify them about changing their queries. Then return a suitable value to tell GraphQL-Ruby how to continue.
Parameters
-
query(GraphQL::Query) -
variable_definition(GraphQL::Language::Nodes::VariableDefinition) β The definition of the variable -
argument(GraphQL::Language::Nodes::Argument) β The argument which uses the variable. You might call.lineand.colfor custom error reporting.
Returns
-
:return_validation_errorβ Let GraphQL-Ruby return the (new) normal validation error for this query -
Stringβ A validation error to return for this query -
nilβ Donβt send the client an error, continue the legacy behavior (allow this query to execute)
Source
# File lib/graphql/schema.rb, line 1661 def load_type(type_name, ctx) get_type(type_name, ctx) end
Called when a type is needed by name at runtime
# File lib/graphql/schema.rb, line 1408 def logger_for(context) if context && context[:logger] == false Logger.new(IO::NULL) elsif context && (l = context[:logger]) l else default_logger end end
Parameters
-
context(GraphQL::Query::Context, nil)
Returns
-
Loggerβ A logger to use for this context configuration, falling back to.default_logger
# File lib/graphql/schema.rb, line 1182 def max_complexity(max_complexity = nil, count_introspection_fields: true) if max_complexity @max_complexity = max_complexity @max_complexity_count_introspection_fields = count_introspection_fields elsif defined?(@max_complexity) @max_complexity else find_inherited_value(:max_complexity) end end
# File lib/graphql/schema.rb, line 1193 def max_complexity_count_introspection_fields if defined?(@max_complexity_count_introspection_fields) @max_complexity_count_introspection_fields else find_inherited_value(:max_complexity_count_introspection_fields, true) end end
# File lib/graphql/schema.rb, line 1220 def max_depth(new_max_depth = nil, count_introspection_fields: true) if new_max_depth @max_depth = new_max_depth @count_introspection_fields = count_introspection_fields elsif defined?(@max_depth) @max_depth else find_inherited_value(:max_depth) end end
# File lib/graphql/schema.rb, line 1066 def max_query_string_tokens(new_max_tokens = NOT_CONFIGURED) if NOT_CONFIGURED.equal?(new_max_tokens) defined?(@max_query_string_tokens) ? @max_query_string_tokens : find_inherited_value(:max_query_string_tokens) else @max_query_string_tokens = new_max_tokens end end
A limit on the number of tokens to accept on incoming query strings. Use this to prevent parsing maliciously-large query strings.
Returns
-
nil, Integer
# File lib/graphql/schema.rb, line 2143 def multiplex(queries, **kwargs) if @default_execution_next multiplex_next(queries, **kwargs) else GraphQL::Execution::Interpreter.run_all(self, queries, **kwargs) end end
Execute several queries on itself, concurrently.
See the GraphQL::Query constructor for query keyword arguments. Multiplex-level execution is handled by the interpreterβs GraphQL::Execution::Interpreter.run_all method.
Examples
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], }
Parameters
-
queries(Array<Hash>) β Keyword arguments for each query
Options
-
kwargs.:context(Hash) β ({}) Multiplex-level context -
kwargs.:max_complexity(nil, Integer) β (nil)
Returns
-
Array<GraphQL::Query::Result>β One result for each query in the input
# File lib/graphql/schema.rb, line 2046 def multiplex_analyzer(new_analyzer) own_multiplex_analyzers << new_analyzer end
See GraphQL::Analysis the analysis system
Parameters
-
new_analyzer(Class<GraphQL::Analysis::Analyzer>) β An analyzer to run on multiplexes to this schema
Source
# File lib/graphql/schema.rb, line 2050 def multiplex_analyzers find_inherited_value(:multiplex_analyzers, EMPTY_ARRAY) + own_multiplex_analyzers end
# File lib/graphql/schema.rb, line 719 def mutation(new_mutation_object = nil, &lazy_load_block) if new_mutation_object || block_given? if @mutation_object dup_defn = new_mutation_object || yield raise GraphQL::Error, "Second definition of `mutation(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@mutation_object.inspect}" elsif use_visibility_profile? if block_given? if visibility.preload? @mutation_object = lazy_load_block.call self.visibility.mutation_configured(@mutation_object) else @mutation_object = lazy_load_block end else @mutation_object = new_mutation_object self.visibility.mutation_configured(@mutation_object) end else @mutation_object = new_mutation_object || lazy_load_block.call add_type_and_traverse(@mutation_object, root: true) end nil elsif @mutation_object.is_a?(Proc) @mutation_object = @mutation_object.call self.visibility&.mutation_configured(@mutation_object) @mutation_object else @mutation_object || find_inherited_value(:mutation) end end
Get or set the root mutation object for this schema.
Examples
Example: Using Types::Mutation as the entry-point
mutation { Types::Mutation }
Parameters
-
new_mutation_object(Class<GraphQL::Schema::Object>) β The root type to use for mutations -
lazy_load_blockβ If a block is given, then it will be called when GraphQL-Ruby needs the root mutation type.
Returns
-
Class<GraphQL::Schema::Object>, nilβ The configured mutation root type, if there is one.
Source
# File lib/graphql/schema.rb, line 1094 def mutation_execution_strategy(new_mutation_execution_strategy = nil, deprecation_warning: true) if deprecation_warning warn "GraphQL::Schema.mutation_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead." warn " #{caller(1, 1).first}" end if new_mutation_execution_strategy @mutation_execution_strategy = new_mutation_execution_strategy else @mutation_execution_strategy || (superclass.respond_to?(:mutation_execution_strategy) ? superclass.mutation_execution_strategy(deprecation_warning: false) : self.default_execution_strategy) end end
# File lib/graphql/schema.rb, line 1992 def new_trace(mode: nil, **options) should_sample = if detailed_trace if (query = options[:query]) detailed_trace?(query) elsif (multiplex = options[:multiplex]) if multiplex.queries.length == 1 detailed_trace?(multiplex.queries.first) else detailed_trace?(multiplex) end end else false end if should_sample mode = detailed_trace.trace_mode else target = options[:query] || options[:multiplex] mode ||= target && target.context[:trace_mode] end trace_mode = mode || default_trace_mode base_trace_options = trace_options_for(trace_mode) trace_options = base_trace_options.merge(options) trace_class_for_mode = trace_class_for(trace_mode, build: true) trace_class_for_mode.new(**trace_options) end
Create a trace instance which will include the trace modules specified for the optional mode.
If no mode: is given, then default_trace_mode will be used.
If this schema is using Tracing::DetailedTrace and .detailed_trace? returns true, then DetailedTraceβs mode will override the passed-in mode.
Parameters
-
mode(Symbol) β Trace modules for this trade mode will be included -
options(Hash) β Keywords that will be passed to the tracing class during#initialize
Returns
Source
# File lib/graphql/schema.rb, line 520 def null_context @null_context || GraphQL::Query::NullContext.instance end
# File lib/graphql/schema.rb, line 1613 def object_from_id(object_id, context) raise GraphQL::RequiredImplementationMissingError, "#{self.name}.object_from_id(object_id, context) must be implemented to load by ID (tried to load from id `#{object_id}`)" end
Fetch an object based on an incoming ID and the current context. This method should return an object from your application, or return nil if there is no object or the object shouldnβt be available to this operation.
See id_from_object which produces these IDs
Examples
Example: Fetching an object with Railsβs GlobalID
def self.object_from_id(object_id, _context) GlobalID.find(global_id) # TODO: use `context[:current_user]` to determine if this object is authorized. end
Parameters
-
object_id(String) β The ID to fetch an object for. This may be client-provided (as innode(id: ...)orloads:) or previously stored by the schema (eg, by theObjectCache) -
context(GraphQL::Query::Context) β The context for the currently-executing operation
Returns
-
Object, nilβ The application whichobject_idreferences, ornilif there is no object or the current operation shouldnβt have access to the object
# File lib/graphql/schema.rb, line 1324 def orphan_types(*new_orphan_types) if !new_orphan_types.empty? new_orphan_types = new_orphan_types.flatten non_object_types = new_orphan_types.reject { |ot| ot.is_a?(Class) && ot < GraphQL::Schema::Object } if !non_object_types.empty? raise ArgumentError, <<~ERR Only object type classes should be added as `orphan_types(...)`. - Remove these no-op types from `orphan_types`: #{non_object_types.map { |t| "#{t.inspect} (#{t.kind.name})"}.join(", ")} - See https://graphql-ruby.org/type_definitions/interfaces.html#orphan-types To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types ERR end add_type_and_traverse(new_orphan_types, root: false) unless use_visibility_profile? own_orphan_types.concat(new_orphan_types.flatten) self.visibility&.orphan_types_configured(new_orphan_types) end inherited_ot = find_inherited_value(:orphan_types, nil) if inherited_ot if !own_orphan_types.empty? inherited_ot + own_orphan_types else inherited_ot end else own_orphan_types end end
Tell the schema about these types so that they can be registered as implementations of interfaces in the schema.
This method must be used when an object type is connected to the schema as an interface implementor but not as a return type of a field. In that case, if the object type isnβt registered here, GraphQL-Ruby wonβt be able to find it.
Parameters
-
new_orphan_types(Array<Class<GraphQL::Schema::Object>>) βObjecttypes to register as implementations of interfaces in the schema.
Returns
-
Array<Class<GraphQL::Schema::Object>>β All previously-registered orphan types for this schema
Source
# File lib/graphql/schema.rb, line 344 def own_trace_modes @own_trace_modes ||= {} end
Source
# File lib/graphql/schema.rb, line 376 def own_trace_modules @own_trace_modules ||= Hash.new { |h, k| h[k] = [] } end
# File lib/graphql/schema.rb, line 1767 def parse_error(parse_err, ctx) ctx.errors.push(parse_err) end
A function to call when .execute receives an invalid query string
The default is to add the error to context.errors
Parameters
-
parse_err(GraphQL::ParseError) β The error encountered during parsing -
ctx(GraphQL::Query::Context) β The context for the query where the error occurred
Returns
-
Objectβ void
Source
# File lib/graphql/schema.rb, line 514 def plugins find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins end
# File lib/graphql/schema.rb, line 879 def possible_types(type = nil, context = null_context, use_visibility_profile = use_visibility_profile?) if use_visibility_profile if type return Visibility::Profile.from_context(context, self).possible_types(type) else raise "Schema.possible_types is not implemented for `use_visibility_profile?`" end end 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] visible_possible_types = if stored_possible_types && type.kind.interface? stored_possible_types.select do |possible_type| possible_type.interfaces(context).include?(type) end else stored_possible_types end visible_possible_types || introspection_system.possible_types[type] || ( superclass.respond_to?(:possible_types) ? superclass.possible_types(type, context, use_visibility_profile) : EMPTY_ARRAY ) end else find_inherited_value(:possible_types, EMPTY_HASH) .merge(own_possible_types) .merge(introspection_system.possible_types) end end
Parameters
-
type(Module) β The type definition whose possible types you want to see -
context(GraphQL::Query::Context) β used for filtering visible possible types at runtime -
use_visibility_profileβ Private, for migration toSchema::Visibility
Returns
-
Hash<String, Module>β All possible types, if notypeis given. -
Array<Module>β Possible types fortype, if itβs given.
# File lib/graphql/schema.rb, line 667 def query(new_query_object = nil, &lazy_load_block) if new_query_object || block_given? if @query_object dup_defn = new_query_object || yield raise GraphQL::Error, "Second definition of `query(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@query_object.inspect}" elsif use_visibility_profile? if block_given? if visibility.preload? @query_object = lazy_load_block.call self.visibility.query_configured(@query_object) else @query_object = lazy_load_block end else @query_object = new_query_object self.visibility.query_configured(@query_object) end else @query_object = new_query_object || lazy_load_block.call add_type_and_traverse(@query_object, root: true) end nil elsif @query_object.is_a?(Proc) @query_object = @query_object.call self.visibility&.query_configured(@query_object) @query_object else @query_object || find_inherited_value(:query) end end
Get or set the root query object for this schema.
Examples
Example: Using Types::Query as the entry-point
query { Types::Query }
Parameters
-
new_query_object(Class<GraphQL::Schema::Object>) β The root type to use for queries -
lazy_load_blockβ If a block is given, then it will be called when GraphQL-Ruby needs the root query type.
Returns
-
Class<GraphQL::Schema::Object>, nilβ The configured query root type, if there is one.
# File lib/graphql/schema.rb, line 2029 def query_analyzer(new_analyzer) own_query_analyzers << new_analyzer end
See GraphQL::Analysis the analysis system
Parameters
-
new_analyzer(Class<GraphQL::Analysis::Analyzer>) β An analyzer to run on queries to this schema
Source
# File lib/graphql/schema.rb, line 2033 def query_analyzers inherited_qa = find_inherited_value(:query_analyzers, EMPTY_ARRAY) inherited_qa.empty? ? own_query_analyzers : (inherited_qa + own_query_analyzers) end
# File lib/graphql/schema.rb, line 1162 def query_class(new_query_class = NOT_CONFIGURED) if NOT_CONFIGURED.equal?(new_query_class) @query_class || (superclass.respond_to?(:query_class) ? superclass.query_class : GraphQL::Query) else @query_class = new_query_class end end
Parameters
-
new_query_class(Class<GraphQL::Query>) β A subclass to use when executing queries
# File lib/graphql/schema.rb, line 1082 def query_execution_strategy(new_query_execution_strategy = nil, deprecation_warning: true) if deprecation_warning warn "GraphQL::Schema.query_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead." warn " #{caller(1, 1).first}" end if new_query_execution_strategy @query_execution_strategy = new_query_execution_strategy else @query_execution_strategy || (superclass.respond_to?(:query_execution_strategy) ? superclass.query_execution_strategy(deprecation_warning: false) : self.default_execution_strategy) end end
# File lib/graphql/schema.rb, line 2196 def query_stack_error(query, err) query.context.errors.push(GraphQL::ExecutionError.new("This query is too large to execute.")) end
Called when execution encounters a SystemStackError. By default, it adds a client-facing error to the response. You could modify this method to report this error to your bug tracker.
Parameters
-
query(GraphQL::Query) -
err(SystemStackError)
Returns
-
void
# File lib/graphql/schema.rb, line 937 def references_to(to_type = nil, from: nil) if to_type if from refs = own_references_to[to_type] ||= [] refs << from else get_references_to(to_type) || EMPTY_ARRAY 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.empty? inherited_value.merge(own_references_to) else own_references_to end end end
# File lib/graphql/schema.rb, line 1462 def rescue_from(*err_classes, &handler_block) err_classes.each do |err_class| Execution::Errors.register_rescue_from(err_class, error_handlers[:subclass_handlers], handler_block) end end
Register a handler for errors raised during execution. The handlers can return a new value or raise a new error.
Examples
Example: Handling βnot foundβ with a client-facing error
rescue_from(ActiveRecord::NotFound) { raise GraphQL::ExecutionError, "An object could not be found" }
Parameters
-
err_classes(Array<StandardError>) β Classes which should be rescued byhandler_block -
handler_blockβ The code to run when one of those errors is raised during execution
Yields
-
error(StandardError) β An instance of one of the configurederr_classes -
object(Object) β The current application object in the query when the error was raised -
arguments(GraphQL::Query::Arguments) β The current field arguments when the error was raised -
context(GraphQL::Query::Context) β The context for the currently-running operation -
Objectβ Some object to use in the place where this error was raised
Raises
-
GraphQL::ExecutionErrorβ In the handler, raise to add a client-facing error to the response -
StandardErrorβ In the handler, raise to crash the query with a developer-facing error
# File lib/graphql/schema.rb, line 1561 def resolve_type(abstract_type, application_object, context) raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(abstract_type, application_object, context) must be implemented to use Union types, Interface types, `loads:`, or `run_partials` (tried to resolve: #{abstract_type.name})" end
GraphQL-Ruby calls this method during execution when it needs the application to determine the type to use for an object.
Usually, this object was returned from a field whose return type is an GraphQL::Schema::Interface or a GraphQL::Schema::Union. But this method is called in other cases, too β for example, when GraphQL::Schema::Argument#loads cases an object to be directly loaded from the database.
Examples
Example: Returning a GraphQL type based on the objectβs class name
class MySchema < GraphQL::Schema def resolve_type(_abs_type, object, _context) graphql_type_name = "Types::#{object.class.name}Type" graphql_type_name.constantize # If this raises a NameError, then come implement special cases in this method end end
Parameters
-
abstract_type(Class, Module, nil) β TheInterfaceorUniontype which is being resolved, if there is one -
application_object(Object) β The object returned from a field whose type must be determined -
context(GraphQL::Query::Context) β The query context for the currently-executing query
Returns
-
Class<GraphQL::Schema::Object>β TheObjecttype definition to use forobj
Source
# File lib/graphql/schema.rb, line 1775 def resolves_lazies? lazy_method_count = 0 lazy_methods.each do |k, v| if !v.nil? lazy_method_count += 1 end end lazy_method_count > 2 end
Source
# File lib/graphql/schema.rb, line 824 def root_types if use_visibility_profile? [query, mutation, subscription].compact else @root_types end end
Returns
-
Array<Class>β The root types (query, mutation, subscription) defined for this schema
# File lib/graphql/schema.rb, line 2054 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
# File lib/graphql/schema.rb, line 1651 def schema_directive(dir_class, **options) @own_schema_directives ||= [] Member::HasDirectives.add_directive(self, @own_schema_directives, dir_class, options) end
Source
# File lib/graphql/schema.rb, line 1656 def schema_directives Member::HasDirectives.get_directives(self, @own_schema_directives, :schema_directives) end
Source
# File lib/graphql/schema.rb, line 489 def static_validator GraphQL::StaticValidation::Validator.new(schema: self) end
# File lib/graphql/schema.rb, line 771 def subscription(new_subscription_object = nil, &lazy_load_block) if new_subscription_object || block_given? if @subscription_object dup_defn = new_subscription_object || yield raise GraphQL::Error, "Second definition of `subscription(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@subscription_object.inspect}" elsif use_visibility_profile? if block_given? if visibility.preload? @subscription_object = lazy_load_block.call visibility.subscription_configured(@subscription_object) else @subscription_object = lazy_load_block end else @subscription_object = new_subscription_object self.visibility.subscription_configured(@subscription_object) end add_subscription_extension_if_necessary else @subscription_object = new_subscription_object || lazy_load_block.call add_subscription_extension_if_necessary add_type_and_traverse(@subscription_object, root: true) end nil elsif @subscription_object.is_a?(Proc) @subscription_object = @subscription_object.call add_subscription_extension_if_necessary self.visibility.subscription_configured(@subscription_object) @subscription_object else @subscription_object || find_inherited_value(:subscription) end end
Get or set the root subscription object for this schema.
Examples
Example: Using Types::Subscription as the entry-point
subscription { Types::Subscription }
Parameters
-
new_subscription_object(Class<GraphQL::Schema::Object>) β The root type to use for subscriptions -
lazy_load_blockβ If a block is given, then it will be called when GraphQL-Ruby needs the root subscription type.
Returns
-
Class<GraphQL::Schema::Object>, nilβ The configured subscription root type, if there is one.
# File lib/graphql/schema.rb, line 1106 def subscription_execution_strategy(new_subscription_execution_strategy = nil, deprecation_warning: true) if deprecation_warning warn "GraphQL::Schema.subscription_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead." warn " #{caller(1, 1).first}" end if new_subscription_execution_strategy @subscription_execution_strategy = new_subscription_execution_strategy else @subscription_execution_strategy || (superclass.respond_to?(:subscription_execution_strategy) ? superclass.subscription_execution_strategy(deprecation_warning: false) : self.default_execution_strategy) end end
# File lib/graphql/schema.rb, line 261 def subscriptions(inherited: true) defined?(@subscriptions) ? @subscriptions : (inherited ? find_inherited_value(:subscriptions, nil) : nil) end
Returns
# File lib/graphql/schema.rb, line 265 def subscriptions=(new_implementation) @subscriptions = new_implementation end
# File lib/graphql/schema.rb, line 449 def to_definition(context: {}) GraphQL::Schema::Printer.print_schema(self, context: context) end
# File lib/graphql/schema.rb, line 461 def to_document GraphQL::Language::DocumentFromSchemaDefinition.new(self).document end
Return the GraphQL::Language::Document IDL AST for the schema
Returns
-
GraphQL::Language::Document
Source
# File lib/graphql/schema.rb, line 404 def to_json(**args) JSON.pretty_generate(as_json(**args)) end
Returns the JSON response of Introspection::INTROSPECTION_QUERY. See as_json Return a Hash representation of the schema
Returns
-
String
# File lib/graphql/schema.rb, line 289 def trace_class(new_class = nil) if new_class # If any modules were already added for `:default`, # re-apply them here mods = trace_modules_for(:default) mods.each { |mod| new_class.include(mod) } new_class.include(DefaultTraceClass) trace_mode(:default, new_class) end trace_class_for(:default, build: true) end
# File lib/graphql/schema.rb, line 307 def trace_class_for(mode, build: false) if (trace_class = own_trace_modes[mode]) trace_class elsif superclass.respond_to?(:trace_class_for) && (trace_class = superclass.trace_class_for(mode, build: false)) trace_class elsif build own_trace_modes[mode] = build_trace_mode(mode) else nil end end
Returns
-
Classβ Return the trace class to use for this mode, looking one up on the superclass if thisSchemadoesnβt have one defined.
# File lib/graphql/schema.rb, line 339 def trace_mode(mode_name, trace_class) own_trace_modes[mode_name] = trace_class nil end
Configure trace_class to be used whenever context: { trace_mode: mode_name } is requested. default_trace_mode is used when no trace_mode: ... is requested.
When a trace_class is added this way, it will not receive other modules added with trace_with(...) unless trace_mode is explicitly given. (This class will not receive any default trace modules.)
Subclasses of the schema will use trace_class as a base class for this mode and those subclass also will not receive default tracing modules.
Parameters
-
mode_name(Symbol) -
trace_class(Class) β subclass ofGraphQL::Tracing::Trace
Returns
-
Objectβ void
# File lib/graphql/schema.rb, line 386 def trace_modules_for(trace_mode) modules = own_trace_modules[trace_mode] if superclass.respond_to?(:trace_modules_for) modules += superclass.trace_modules_for(trace_mode) end modules end
Returns
-
Array<Module>β Modules added for tracing intrace_mode, including inherited ones
# File lib/graphql/schema.rb, line 1959 def trace_options_for(mode) @trace_options_for_mode ||= {} @trace_options_for_mode[mode] ||= begin # It may be time to create an options hash for a mode that wasn't registered yet. # Mix in the default options in that case. default_options = mode == :default ? EMPTY_HASH : trace_options_for(:default) # Make sure this returns a new object so that other hashes aren't modified later if superclass.respond_to?(:trace_options_for) superclass.trace_options_for(mode).merge(default_options) else default_options.dup end end end
The options hash for this trace mode
Returns
-
Hash
# File lib/graphql/schema.rb, line 1923 def trace_with(trace_mod, mode: :default, **options) if mode.is_a?(Array) mode.each { |m| trace_with(trace_mod, mode: m, **options) } else tc = own_trace_modes[mode] ||= build_trace_mode(mode) tc.include(trace_mod) own_trace_modules[mode] << trace_mod add_trace_options_for(mode, options) if mode == :default # This module is being added as a default tracer. If any other mode classes # have already been created, but get their default behavior from a superclass, # Then mix this into this schema's subclass. # (But don't mix it into mode classes that aren't default-based.) own_trace_modes.each do |other_mode_name, other_mode_class| if other_mode_class < DefaultTraceClass # Don't add it back to the inheritance tree if it's already there if !(other_mode_class < trace_mod) other_mode_class.include(trace_mod) end # Add any options so they'll be available add_trace_options_for(other_mode_name, options) end end end end nil end
Mix trace_mod into this schemaβs Trace class so that its methods will be called at runtime.
You can attach a module to run in only some circumstances by using mode:. When a module is added with mode:, it will only run for queries with a matching context[:trace_mode].
Any custom trace modes also include the default trace_with ... modules (that is, those added without any particular mode: ... configuration).
See GraphQL::Tracing::Trace for available tracing methods.
Examples
Example: Adding a trace in a special mode
# only runs when `query.context[:trace_mode]` is `:special` trace_with SpecialTrace, mode: :special
Parameters
-
trace_mod(Module) β A module that implements tracing methods -
mode(Symbol) β Trace module will only be used for this trade mode -
options(Hash) β Keywords that will be passed to the tracing class during#initialize
Returns
-
void
# File lib/graphql/schema.rb, line 1867 def tracer(new_tracer, silence_deprecation_warning: false) if !silence_deprecation_warning warn("`Schema.tracer(#{new_tracer.inspect})` is deprecated; use module-based `trace_with` instead. See: https://graphql-ruby.org/queries/tracing.html") warn " #{caller(1, 1).first}" end default_trace = trace_class_for(:default, build: true) if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers) trace_with(GraphQL::Tracing::CallLegacyTracers) end own_tracers << new_tracer end
Source
# File lib/graphql/schema.rb, line 1880 def tracers inherited = find_inherited_value(:tracers, EMPTY_ARRAY) if inherited.length > 0 if own_tracers.length > 0 inherited + own_tracers else inherited end else own_tracers end end
# File lib/graphql/schema.rb, line 1737 def type_error(type_error, context) case type_error when GraphQL::InvalidNullError execution_error = GraphQL::ExecutionError.new(type_error.message, ast_nodes: type_error.ast_nodes) execution_error.path = type_error.path || context[:current_path] context.errors << execution_error execution_error when GraphQL::UnresolvedTypeError, GraphQL::StringEncodingError, GraphQL::FloatEncodingError, GraphQL::IntegerEncodingError raise type_error when GraphQL::FloatDecodingError, GraphQL::IntegerDecodingError nil end end
Called at runtime when GraphQL-Ruby encounters a mismatch between the application behavior and the GraphQL type system.
The default implementation of this method is to follow the GraphQL specification, but you can override this to report errors to your bug tracker or customize error handling.
Parameters
-
type_error(GraphQL::Error) β several specific error classes are passed here, see the default implementation for details -
context(GraphQL::Query::Context) β the context for the currently-running operation
Returns
-
void
Raises
-
GraphQL::ExecutionErrorβ to return this error to the client -
GraphQL::Errorβ to crash the query and raise a developer-facing error
# File lib/graphql/schema.rb, line 958 def type_from_ast(ast_node, context: self.query_class.new(self, "{ __typename }").context) GraphQL::Schema::TypeExpression.build_type(context.query.types, ast_node) end
# File lib/graphql/schema.rb, line 533 def types(context = null_context) if use_visibility_profile? types = Visibility::Profile.from_context(context, self) return types.all_types_h end 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.new( duplicated_name: k, duplicated_definition_1: visible_t.inspect, duplicated_definition_2: t.inspect ) end end end visible_t else v end end visible_types end
# File lib/graphql/schema.rb, line 915 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.transform_values(&:dup) find_inherited_value(:union_memberships, EMPTY_HASH).each do |k, v| um = joined_um[k] ||= [] um.concat(v) end joined_um end end
# File lib/graphql/schema.rb, line 505 def use(plugin, **kwargs) if !kwargs.empty? plugin.use(self, **kwargs) else plugin.use(self) end own_plugins << [plugin, kwargs] end
# File lib/graphql/schema.rb, line 1142 def validate(string_or_document, rules: nil, context: nil) doc = if string_or_document.is_a?(String) GraphQL.parse(string_or_document, max_tokens: max_query_string_tokens) else string_or_document end query = query_class.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 a query string according to this schema.
Parameters
-
string_or_document(String, GraphQL::Language::Nodes::Document)
Returns
-
Array<GraphQL::StaticValidation::Error >
# File lib/graphql/schema.rb, line 1172 def validate_max_errors(new_validate_max_errors = NOT_CONFIGURED) if NOT_CONFIGURED.equal?(new_validate_max_errors) defined?(@validate_max_errors) ? @validate_max_errors : find_inherited_value(:validate_max_errors) else @validate_max_errors = new_validate_max_errors end end
# File lib/graphql/schema.rb, line 1120 def validate_timeout(new_validate_timeout = NOT_CONFIGURED) if !NOT_CONFIGURED.equal?(new_validate_timeout) @validate_timeout = new_validate_timeout elsif defined?(@validate_timeout) @validate_timeout else find_inherited_value(:validate_timeout) || 3 end end
Source
# File lib/graphql/schema.rb, line 1647 def visible?(member, ctx) member.visible?(ctx) end