Complexity-based Rate Limiting
This feature is still in alpha, because finding out if you set the right complexity score still takes a while.
If you want to define rate limits based on the complexity of the queries users send in, you will need to configure a QueryComplexity
limit.
Same as with the RequestCount
limit, Stellate will check how much of a consumer’s complexity budget was already used up in the time window defined by you. Based on that value and the estimated complexity of the current query, the request is either allowed or denied.
import { Config } from 'stellate'
const config: Config = {
config: {
getConsumerIdentifiers: (req) => {
return {
ip: req.ip,
}
},
rateLimits: [
{
name: 'IP Limit',
consumerIdentifier: 'ip',
// Allow 1000 complexity points per 60s sliding window
limit: {
type: 'QueryComplexity',
window: '60s',
budget: 1000,
},
},
],
},
}
export default config
Limit maximum query complexity
Any QueryComplexity
budget configured via rate limiting, implicitly sets an
upper bound for the overal complexity as well: If you allow 1000 complexity
points per 60s, operations that have more than 1000 complexity points will be
always be blocked.
Additionally, you can also limit the maximum complexity any single operation passing through Stellate can have:
import { Config } from 'stellate'
const config: Config = {
config: {
complexity: {
maxComplexity: 1000,
},
},
}
export default config
How to determine the query complexity budget
You can see the complexity of one of your GraphQL queries by opening GraphiQL on your Stellate dashboard. This is currently only visible if the complexity
configuration option is defined in your Stellate configuration.
Update your configuration with complexity: {}
to trigger this UI to be visible:
import { Config } from 'stellate'
const config: Config = {
config: {
complexity: {},
},
}
export default config
We recommend pasting in your largest query (with the highest pagination arguments you regularly use) and using its complexity as the baseline:
Query Complexity Points
Query complexity takes the number of fields as well as the depth and any pagination arguments into account. Every scalar field adds 1 point, every nested field adds 2 points, and every pagination argument multiplies the nested objects score by the number of records fetched.
Here is an example:
query {
# Total: 18
todos(limit: 2) {
# (Nested: 2 + 1 + 1 + 1 + (author: 2 + 1 + 1)) * limit: 2 = 18
id # Scalar: 1
text # Scalar: 1
completed # Scalar: 1
author {
## Nested: 4 (2 + 1 + 1)
id ## Scalar: 1
name ## Scalar: 1
}
}
}
By default, it takes any argument named ['first', 'last', 'limit', 'pageSize', 'take']
into account. You can change this default vai the complexity.listSizeArguments
configuration option.
To check out how the complexity is calculated for a specific query, you can try any query out both in the hosted and the dashboard GraphiQL: