class GraphQL::Query
A combination of query string and Schema instance which can be reduced to a result.
The API-specific portions of guides/queries/executing_queries.md were migrated to Schema#execute, Schema#multiplex, and this initializer. Those APIs accept query strings or parsed documents, variables, context, root values, operation names, validation controls, and depth/complexity limits. The guide remains a standalone execution tutorial.
API-specific portions
GraphQL::Schema.execute runs one query and GraphQL::Schema.multiplex runs several queries with shared multiplex-level context. Both delegate to this class, so the following options have the same meaning in each API:
-
queryor the positional query string is aGraphQLdocument to parse. -
document:accepts an already-parsedGraphQL::Language::Nodes::Document; pass one or the other, not both. -
variables:is aHashof values for$-named variables. Keys omit the leading$; values may contain nested input objects. -
context:contains application values made available to field resolvers, schema hooks, and Query::Context. -
root_value:is passed asobjto root-level fields. -
operation_name:selects which named operation to execute when a document contains more than one operation. -
validate: falseskips static validation. Use this only when the document has already been validated; execution of an invalid document is undefined. -
max_depth:andmax_complexity:override the corresponding schema limits for this query.niluses the schema configuration.
The executing queries guide keeps examples for variables, context, scoped context, and root values. This class and the Schema#execute/Schema#multiplex method comments are the source of truth for option names, defaults, and execution behavior.
Attributes
Returns
-
nil, String— The operation name provided by client or the one inferred from the document. Used to determine which operation to run.
The value for root types
Returns
-
GraphQL::StaticValidation::Validator— if present, the query will validate with these rules.
Returns
-
String, nil— the triggered event, if this query is a subscription update
Returns
-
Boolean— if false, static validation is skipped (execution behavior for invalid queries is undefined)
Returns
-
Symbol, nil
Public Class Methods
# File lib/graphql/query.rb, line 213 def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, multiplex: nil, validate: true, static_validator: nil, visibility_profile: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil, use_visibility_profile: nil) # Even if `variables: nil` is passed, use an empty hash for simpler logic variables ||= {} @multiplex = multiplex @schema = schema @context = schema.context_class.new(query: self, values: context) if visibility_profile @context[:visibility_profile] ||= visibility_profile end if use_visibility_profile.nil? use_visibility_profile = warden ? false : schema.use_visibility_profile? end if use_visibility_profile @visibility_profile = @schema.visibility.profile_for(@context) @warden = Schema::Warden::NullWarden.new(context: @context, schema: @schema) else @visibility_profile = nil @warden = warden end @subscription_topic = subscription_topic @root_value = root_value @fragments = nil @operations = nil @finalizers = @top_level_finalizers = nil @validate = validate self.static_validator = static_validator if static_validator context_tracers = (context ? context.fetch(:tracers, []) : []) @tracers = schema.tracers + context_tracers if !context_tracers.empty? && !(schema.trace_class <= GraphQL::Tracing::CallLegacyTracers) raise ArgumentError, "context[:tracers] are not supported without `trace_with(GraphQL::Tracing::CallLegacyTracers)` in the schema configuration, please add it." end @analysis_errors = [] if variables.is_a?(String) raise ArgumentError, "Query variables should be a Hash, not a String. Try JSON.parse to prepare variables." else @provided_variables = variables || {} end @query_string = query_string || query @document = document if @query_string && @document raise ArgumentError, "Query should only be provided a query string or a document, not both." end if @query_string && !@query_string.is_a?(String) raise ArgumentError, "Query string argument should be a String, got #{@query_string.class.name} instead." end # A two-layer cache of type resolution: # { abstract_type => { value => resolved_type } } @resolved_types_cache = Hash.new do |h1, k1| h1[k1] = Hash.new do |h2, k2| h2[k2] = @schema.resolve_type(k1, k2, @context) end end # Trying to execute a document # with no operations returns an empty hash @ast_variables = [] @mutation = false @operation_name = operation_name @prepared_ast = false @validation_pipeline = nil @max_depth = max_depth @max_complexity = max_complexity @result_values = nil @executed = false @logger = schema.logger_for(context) end
Prepare query query_string on schema
Parameters
-
schema(GraphQL::Schema) -
query_string(String) -
context(#[]) — an arbitrary hash of values which you can access in Schema::Field#resolve -
variables(Hash) — values for$variablesin the query -
operation_name(String) — if the query string contains many operations, this is the one which should be executed -
root_value(Object) — the object used to resolve fields on the root type -
max_depth(Numeric) — the maximum number of nested selections allowed for this query (falls back to schema-level value) -
max_complexity(Numeric) — the maximum field complexity for this query (falls back to schema-level value) -
visibility_profile(Symbol) — Another way to assigncontext[:visibility_profile]
Public Instance Methods
# File lib/graphql/query.rb, line 309 def current_trace @current_trace ||= context[:trace] || (multiplex ? multiplex.current_trace : schema.new_trace(multiplex: multiplex, query: self)) end
Returns
# File lib/graphql/query.rb, line 166 def document # It's ok if this hasn't been assigned yet if @query_string || @document with_prepared_ast { @document } else nil end end
Source
# File lib/graphql/query.rb, line 476 def fingerprint @fingerprint ||= "#{operation_fingerprint}/#{variables_fingerprint}" end
This contains a few components:
-
The selected operation name (or
anonymous) -
The fingerprint of the query string
-
The number of given variables (for readability)
-
The fingerprint of the given variables
This fingerprint can be used to track runs of the same operation-variables combination over time.
See operation_fingerprint and variables_fingerprint for the components.
Returns
-
String— An opaque hash identifying this operation-variables combination
Source
# File lib/graphql/query.rb, line 346 def fragments with_prepared_ast { @fragments } end
# File lib/graphql/query.rb, line 520 def get_field(owner, field_name) types.field(owner, field_name) # rubocop:disable Development/ContextIsPassedCop end
Source
# File lib/graphql/query.rb, line 516 def get_type(type_name) types.type(type_name) # rubocop:disable Development/ContextIsPassedCop end
# File lib/graphql/query.rb, line 325 def lookahead @lookahead ||= begin if selected_operation.nil? GraphQL::Execution::Lookahead::NULL_LOOKAHEAD else GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [selected_operation]) end end end
Source
# File lib/graphql/query.rb, line 574 def mutation? with_prepared_ast { @mutation } end
# File lib/graphql/query.rb, line 486 def operation_fingerprint @operation_fingerprint ||= "#{selected_operation_name || "anonymous"}/#{Fingerprint.generate(query_string || "")}" end
Returns
-
String— An opaque hash for identifying this query’s given query string and selected operation
Source
# File lib/graphql/query.rb, line 350 def operations with_prepared_ast { @operations } end
Source
# File lib/graphql/query.rb, line 524 def possible_types(type) types.possible_types(type) # rubocop:disable Development/ContextIsPassedCop end
Source
# File lib/graphql/query.rb, line 292 def query_string @query_string ||= (document ? document.to_query_string : nil) end
If a document was provided to GraphQL::Schema#execute instead of the raw query string, we will need to get it from the document
# File lib/graphql/query.rb, line 562 def resolve_type(abstract_type, value = NOT_CONFIGURED) if value.is_a?(Symbol) && value == NOT_CONFIGURED # Old method signature value = abstract_type abstract_type = nil end if value.is_a?(GraphQL::Schema::Object) value = value.object end @resolved_types_cache[abstract_type][value] end
See possible_types to apply filtering from only / except
Parameters
-
abstract_type(GraphQL::UnionType, GraphQL::InterfaceType) -
value(Object) — Any runtime value
Returns
-
GraphQL::ObjectType, nil— The runtime type ofvaluefrom Schema.resolve_type
# File lib/graphql/query.rb, line 391 def result if !@executed execution_engine = @schema.default_execution_next ? Execution::Next : Execution::Interpreter execution_engine.run_all(@schema, [self], context: @context) end @result ||= Query::Result.new(query: self, values: @result_values) end
Get the result for this query, executing it once
Returns
-
GraphQL::Query::Result— A Hash-likeGraphQLresponse, with"data"and/or"errors"keys
Source
# File lib/graphql/query.rb, line 541 def root_type root_type_for_operation(selected_operation.operation_type) end
# File lib/graphql/query.rb, line 528 def root_type_for_operation(op_type) case op_type when "query", nil types.query_root # rubocop:disable Development/ContextIsPassedCop when "mutation" types.mutation_root # rubocop:disable Development/ContextIsPassedCop when "subscription" types.subscription_root # rubocop:disable Development/ContextIsPassedCop else raise ArgumentError, "unexpected root type name: #{op_type.inspect}; expected nil, 'query', 'mutation', or 'subscription'" end end
# File lib/graphql/query.rb, line 374 def run_partials(partials_hashes) partials = partials_hashes.map { |partial_options| Partial.new(**partial_options, query: self) } if context[:__graphql_execute_next] Execution::Next.run_all(@schema, partials, context: @context) else Execution::Interpreter.run_all(@schema, partials, context: @context) end end
Run subtree partials of this query and return their results. Each partial is identified with a path: and object: where the path references a field in the AST and the object will be treated as the return value from that field. Subfields of the field named by path will be executed with object as the starting point
Parameters
-
partials_hashes(Array<Hash{Symbol => Object}>) — Hashes withpath:andobject:keys
Returns
-
Array<GraphQL::Query::Result>
# File lib/graphql/query.rb, line 453 def sanitized_query_string(inline_variables: true) with_prepared_ast { schema.sanitized_printer.new(self, inline_variables: inline_variables).sanitized_query_string } end
A version of the given query string, with: - Variables inlined to the query - Strings replaced with <REDACTED>
Returns
-
String, nil— Returns nil if the query is invalid.
# File lib/graphql/query.rb, line 416 def selected_operation with_prepared_ast { @selected_operation } end
This is the operation to run for this query. If more than one operation is present, it must be named at runtime.
Returns
-
GraphQL::Language::Nodes::OperationDefinition, nil
# File lib/graphql/query.rb, line 185 def selected_operation_name return nil unless selected_operation selected_operation.name end
Returns
-
String, nil— The name of the operation to run (may be inferred)
Source
# File lib/graphql/query.rb, line 403 def static_errors validation_errors + analysis_errors + context.errors end
# File lib/graphql/query.rb, line 148 def static_validator=(new_validator) if defined?(@validation_pipeline) && @validation_pipeline && @validation_pipeline.has_validated? raise ArgumentError, "Can't reassign Query#static_validator= after validation has run, remove this assignment." elsif !new_validator.is_a?(GraphQL::StaticValidation::Validator) raise ArgumentError, "Expected a `GraphQL::StaticValidation::Validator` instance." else @static_validator = new_validator end end
Parameters
-
new_validator(GraphQL::StaticValidation::Validator) — if present, the query will validate with these rules. This can’t be reasssigned after validation.
Source
# File lib/graphql/query.rb, line 582 def subscription? with_prepared_ast { @subscription } end
Source
# File lib/graphql/query.rb, line 313 def subscription_update? @subscription_topic && subscription? end
Source
# File lib/graphql/query.rb, line 545 def types @visibility_profile || warden.visibility_profile end
Source
# File lib/graphql/query.rb, line 508 def valid? validation_pipeline.valid? && analysis_errors.empty? end
Source
# File lib/graphql/query.rb, line 129 def validate=(new_validate) if defined?(@validation_pipeline) && @validation_pipeline && @validation_pipeline.has_validated? raise ArgumentError, "Can't reassign Query#validate= after validation has run, remove this assignment." else @validate = new_validate end end
Parameters
-
new_validate(Boolean) — if false, static validation is skipped. This can’t be reasssigned after validation.
Source
# File lib/graphql/query.rb, line 500 def validation_pipeline with_prepared_ast { @validation_pipeline } end
# File lib/graphql/query.rb, line 431 def variables @variables ||= begin with_prepared_ast { GraphQL::Query::Variables.new( @context, @ast_variables, @provided_variables, ) } end end
Determine the values for variables of this query, using default values if a value isn’t provided at runtime.
If some variable is invalid, errors are added to the query’s validation errors.
Returns
-
GraphQL::Query::Variables—Variablesto apply to this query
# File lib/graphql/query.rb, line 496 def variables_fingerprint @variables_fingerprint ||= "#{provided_variables.size}/#{Fingerprint.generate(provided_variables.to_json)}" end
Returns
-
String— An opaque hash for identifying this query’s given a variable values (not including defaults)