2020-12-18 06:59:37 +00:00
|
|
|
|
# Query complexity and depth
|
|
|
|
|
|
|
|
|
|
⚠️GraphQL provides a powerful way to query your data, but putting great
|
|
|
|
|
power in the hands of your API clients also exposes you to a risk of denial
|
|
|
|
|
of service attacks. You can mitigate that risk with `Async-graphql` by limiting the
|
|
|
|
|
complexity and depth of the queries you allow.
|
|
|
|
|
|
|
|
|
|
## Expensive Queries
|
|
|
|
|
|
|
|
|
|
Consider a schema that allows listing blog posts. Each blog post is also related to other posts.
|
|
|
|
|
|
|
|
|
|
```graphql
|
|
|
|
|
type Query {
|
|
|
|
|
posts(count: Int = 10): [Post!]!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Post {
|
|
|
|
|
title: String!
|
|
|
|
|
text: String!
|
|
|
|
|
related(count: Int = 10): [Post!]!
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
It’s not too hard to craft a query that will cause a very large response:
|
|
|
|
|
|
|
|
|
|
```graphql
|
|
|
|
|
{
|
|
|
|
|
posts(count: 100) {
|
|
|
|
|
related(count: 100) {
|
|
|
|
|
related(count: 100) {
|
|
|
|
|
related(count: 100) {
|
|
|
|
|
title
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The size of the response increases exponentially with every other level of the `related` field. Fortunately, `Async-graphql` provides
|
|
|
|
|
a way to prevent such queries.
|
|
|
|
|
|
|
|
|
|
## Limiting Query depth
|
|
|
|
|
|
|
|
|
|
The depth is the number of nesting levels of the field, and the following is a query with a depth of `3`.
|
|
|
|
|
|
|
|
|
|
```graphql
|
|
|
|
|
{
|
|
|
|
|
a {
|
|
|
|
|
b {
|
|
|
|
|
c
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You can limit the depth when creating `Schema`. If the query exceeds this limit, an error will occur and the
|
|
|
|
|
message `Query is nested too deep` will be returned.
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
|
|
|
|
|
.limit_depth(5) // Limit the maximum depth to 5
|
|
|
|
|
.finish();
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Limiting Query complexity
|
|
|
|
|
|
|
|
|
|
The complexity is the number of fields in the query. The default complexity of each field is `1`. Below is a
|
|
|
|
|
query with a complexity of `6`.
|
|
|
|
|
|
|
|
|
|
```graphql
|
|
|
|
|
{
|
|
|
|
|
a b c {
|
|
|
|
|
d {
|
|
|
|
|
e f
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You can limit the complexity when creating the `Schema`. If the query exceeds this limit, an error will occur
|
|
|
|
|
and `Query is too complex` will be returned.
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
|
|
|
|
|
.limit_complexity(5) // Limit the maximum complexity to 5
|
|
|
|
|
.finish();
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Custom Complexity Calculation
|
|
|
|
|
|
|
|
|
|
There are two ways to customize the complexity for non-list type and list type fields.
|
|
|
|
|
|
|
|
|
|
In the following code, the complexity of the `value` field is `5`. The complexity of the `values` field is `count * child_complexity`,
|
|
|
|
|
`child_complexity` is a special variable that represents the complexity of the subquery, and `count` is the parameter of the field,
|
2020-12-18 15:10:57 +00:00
|
|
|
|
used to calculate the complexity of the `values` field, and the type of the return value must be `usize`.
|
2020-12-18 06:59:37 +00:00
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
struct Query;
|
|
|
|
|
|
|
|
|
|
#[Object]
|
|
|
|
|
impl Query {
|
|
|
|
|
#[graphql(complexity = 5)]
|
|
|
|
|
async fn value(&self) -> i32 {
|
|
|
|
|
todo!()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[graphql(complexity = "count * child_complexity")]
|
2020-12-18 15:10:57 +00:00
|
|
|
|
async fn values(&self, count: usize) -> i32 {
|
2020-12-18 06:59:37 +00:00
|
|
|
|
todo!()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Note: The complexity calculation is done in the validation phase and not in the execution phase, so you don't have to worry about the query
|
|
|
|
|
over-limit causing the execute only part of the query.**
|
|
|
|
|
|