Plugins
Query Complexity
Query Complexity
A single GraphQL query can potentially generate a huge workload for a server, like thousands of database operations which can be used to cause DDoS attacks. In order to limit and keep track of what each GraphQL operation can do, the query complexity plugin allows defining field-level complexity values that works with the graphql-query-complexity library.
To install, add the queryComplexityPlugin
to the makeSchema.plugins
array, along with any other plugins you'd like to include:
1import { makeSchema, queryComplexityPlugin } from 'nexus'23const schema = makeSchema({4 // ... types, etc,5 plugins: [6 // ... other plugins7 queryComplexityPlugin(),8 ],9})
The plugin will install a complexity
property on the output field config:
1export const User = objectType({2 name: 'User',3 definition(t) {4 t.id('id', {5 complexity: 2,6 })7 },8})
And of course, integrate graphql-query-complexity
with your GraphQL server. You can setup with express-graphql
as in the library's example.
Complexity Value
There are two ways to define the complexity:
- A number
- A complexity estimator
The easiest way to specify the complexity is to just provide a number, as in the example code above. The complexity
property can be omitted if its value is 1
, provided that you have a simple estimator of 1 when configuring graphql-query-complexity
like below:
1const complexity = getComplexity({2 // ... other configurations3 estimators: [4 // All undefined complexity values will fallback to 15 simpleEstimator({ defaultComplexity: 1 }),6 ],7})
Another way is with the complexity estimator, which is a function that returns a number, but also provides arguments to compute the final value. The query complexity plugin augments graphql-query-complexity
's default complexity estimator by providing its corresponding nexus types to ensure type-safety. No additional arguments are introduced so the function declaration is still syntactically equal.
Augmented complexity estimator function signature:
1type QueryComplexityEstimatorArgs<TypeName extends string, FieldName extends string> = {2 // The root type the field belongs too3 type: RootValue<TypeName>45 // The GraphQLField that is being evaluated6 field: GraphQLField<RootValue<TypeName>, GetGen<'context'>, ArgsValue<TypeName, FieldName>>78 // The input arguments of the field9 args: ArgsValue<TypeName, FieldName>1011 // The complexity of all child selections for that field12 childComplexity: number13}1415type QueryComplexityEstimator = (options: QueryComplexityEstimatorArgs) => number | void
And you can use it like so:
1export const users = queryField('users', {2 type: list('User'),3 args: {4 count: nonNull(intArg()),5 },6 // This will calculate the complexity based on the count and child complexity.7 // This is useful to prevent clients from querying mass amount of data.8 complexity: ({ args, childComplexity }) => args.count * childComplexity,9 resolve: () => [{ id: '1' }],10})
For more info about how query complexity is computed, please visit graphql-query-complexity.