Query Complexity
Baeta provides a query complexity analysis system that helps protect your GraphQL API from resource-exhausting queries. It calculates the complexity of incoming queries and rejects those that exceed configured limits.
Key Features
- Automatic complexity calculation for all fields
- Customizable complexity per field
- Dynamic limits based on context
- List operation handling with multipliers
- Depth and breadth limitations
- Context-aware complexity rules
Installation
- yarn
- npm
- pnpm
- bun
yarn add @baeta/extension-complexity
npm install @baeta/extension-complexity
pnpm add @baeta/extension-complexity
bun add @baeta/extension-complexity
Basic Setup
- Create the Extension
import { createExtensions } from "@baeta/core";
import { complexityExtension } from "@baeta/extension-complexity";
import type { Context } from "./types/context";
const complexity = complexityExtension<Context>({
// Default complexity score for fields
defaultComplexity: 1,
// Multiplier applied to list fields
defaultListMultiplier: 10,
// Dynamic limits based on context
async limit(ctx) {
return {
depth: 10, // Maximum query depth
breadth: 50, // Maximum number of fields at each level
complexity: 1000, // Maximum total complexity score
};
},
// Alternatively, use static limits
// limit: {
// depth: 10,
// breadth: 50,
// complexity: 1000,
// }
});
- Afterwards register it
import { createExtensions } from "@baeta/core";
import { complexity } from "./complexity-extension";
import { authExt } from "./auth-extension";
export default createExtensions(
complexity,
authExt,
//... other extensions
);
tip
Consider placing complexity checks before authorization. This can prevent unnecessary permission checks on queries that would be rejected for being too complex anyway.
- Point Baeta to registered extensions (if you haven't already)
export default defineConfig({
graphql: {
extensions: "src/extensions/index.ts",
// ... other config
},
});
Customizing Complexity Rules
Field-Level Configuration
You can customize complexity rules for specific fields or types:
import { getUserModule } from "./typedef";
const { Query } = getUserModule();
// Disable complexity calculation for specific field
Query.user.$complexity(() => false);
// Custom complexity score for specific field
Query.users.$complexity(({ args, ctx }) => ({
complexity: 1, // Base complexity score
multiplier: 5, // Custom multiplier for list operations
}));
Dynamic Complexity Rules
Complexity rules can be determined dynamically based on context or arguments:
Query.users.$complexity(({ args, ctx }) => {
return {
complexity: 1,
multiplier: args.limit,
};
});
How Complexity is Calculated
- Each field has a base complexity (default: 1)
- List fields are multiplied by the list multiplier
- Nested fields add to the total complexity
- The total is compared against the configured limit
Example:
query {
users(first: 10) {
# complexity: 1 * 10 (multiplier)
name # complexity: 1 * 10 (inherited multiplier)
posts {
# complexity: 1 * 10 * 10 (nested list)
title # complexity: 1 * 10 * 10 (inherited multiplier)
}
}
}
# Total complexity: 210