Ahead-of-Time Analysis

You can provide logic for validating incoming queries and rejecting them if they don’t pass. Query analyzers inspect the query and may return GraphQL::AnalysisError to halt execution.

GraphQL’s max_depth and max_complexity are implemented with query analyzers, you can see those for reference:

Analyzer API

A query analyzer visits each field in the query before the query is executed. It can accumulate data during the visits, then return a value. If the returned value is a GraphQL::AnalysisError (or an array of those errors), the query won’t be executed and the error will be returned to the user. You can use this feature to assert that queries are permitted before running them!

Query analyzers reuse concepts from Array#reduce, so let’s briefly revisit how that method works:

items = [1, 2, 3, 4, 5]
initial_value = 0
reduce_result = items.reduce(initial_value) { |memo, item| memo + item }
final_value = "Sum: #{reduce_result}"
puts final_value
# Sum: 15

A query analyzer has the same basic parts. Here’s the scaffold for an analyzer:

class MyQueryAnalyzer
  # Called before initializing the analyzer.
  # Returns true to run this analyzer, or false to skip it.
  def analyze?(query)
  end

  # Called before the visit.
  # Returns the initial value for `memo`
  def initial_value(query)
  end

  # This is like the `reduce` callback.
  # The return value is passed to the next call as `memo`
  def call(memo, visit_type, irep_node)
  end

  # Called when we're done the whole visit.
  # The return value may be a GraphQL::AnalysisError (or an array of them).
  # Or, you can use this hook to write to a log, etc
  def final_value(memo)
  end
end

Query analyzers are added to the schema with query_analyzer, for example:

class MySchema < GraphQL::Schema
  query_analyzer MyQueryAnalyzer.new
end