Fields have a “complexity” value which can be configured in their definition. It can be a constant (numeric) value, or a proc. If no complexity
is defined for a field, it will default to a value of 1
. It can be defined as a keyword or inside the configuration block. For example:
# Constant complexity:
field :top_score, Integer, null: false, complexity: 10
# Dynamic complexity:
field :top_scorers, [PlayerType], null: false do
argument :limit, Integer, limit: false, default_value: 5
complexity ->(ctx, args, child_complexity) {
if ctx[:current_user].staff?
# no limit for staff users
0
else
# `child_complexity` is the value for selections
# which were made on the items of this list.
#
# We don't know how many items will be fetched because
# we haven't run the query yet, but we can estimate by
# using the `limit` argument which we defined above.
args[:limit] * child_complexity
end
}
end
Then, define your max_complexity
at the schema-level:
class MySchema < GraphQL::Schema
# ...
max_complexity 100
end
Or, at the query-level, which overrides the schema-level setting:
MySchema.execute(query_string, max_complexity: 100)
Using nil
will disable the validation:
# 😧 Anything goes!
MySchema.execute(query_string, max_complexity: nil)
To get a feeling for complexity of queries in your system, you can use the QueryComplexity
query reducer. Hook it up to log out values from each query:
class MySchema < GraphQL::Schema
log_query_complexity = GraphQL::Analysis::QueryComplexity.new { |query, complexity| Rails.logger.info("[GraphQL Query Complexity] #{complexity} | staff? #{query.context[:current_user].staff?}")}
query_analyzer(log_query_complexity)
end
You can also reject queries based on the depth of their nesting. You can define max_depth
at schema-level or query-level:
# Schema-level:
class MySchema < GraphQL::Schema
# ...
max_depth 10
end
# Query-level, which overrides the schema-level setting:
MySchema.execute(query_string, max_depth: 10)
You can use nil
to disable the validation:
# This query won't be validated:
MySchema.execute(query_string, max_depth: nil)
To get a feeling for depth of queries in your system, you can use the QueryDepth
query reducer. Hook it up to log out values from each query:
class MySchema < GraphQL::Schema
log_query_depth = GraphQL::Analysis::QueryDepth.new { |query, depth| Rails.logger.info("[GraphQL Query Depth] #{depth} || staff? #{query.context[:current_user].staff?}")}
query_analyzer(log_query_depth)
end