Security: Depth Limiting and Query Cost
In this tutorial, you will learn about Security: Depth Limiting and Query Cost. We cover key concepts, practical examples, and best practices to help you master this topic.
GraphQL's flexibility makes it vulnerable to denial-of-service attacks through deeply nested queries, expensive field combinations, and circular queries. Security middleware prevents abuse by limiting query depth, cost, and execution time.
What You'll Learn
- Query depth limiting
- Query complexity/cost analysis
- Rate Limiting for GraphQL endpoints
- Persisted queries for production APIs
- Timeout and execution budgets
Why It Matters
A malicious client can craft a query that grows exponentially (e.g., friends.friends.friends). Without security measures, such queries can crash your server or exhaust database connections.
Real-World Use
GitHub sets a depth limit of 7 on their GraphQL API. Shopify uses query cost analysis to reject expensive queries before execution. Apollo Studio provides cost analysis tools for production graphs.
flowchart LR
Request[Incoming Query] --> DepthCheck[Depth Limit Check]
DepthCheck -->|Pass| CostCheck[Complexity Analysis]
DepthCheck -->|Fail| Reject[Reject: Too Deep]
CostCheck -->|Pass| RateLimit[Rate Limit Check]
CostCheck -->|Fail| Reject2[Reject: Too Expensive]
RateLimit -->|Pass| Execute[Execute Query]
RateLimit -->|Fail| Reject3[Reject: Rate Limited]
Teacher Mindset
Treat security as a layered defense. Depth limiting catches obviously malicious queries. Complexity analysis catches expensive but legitimate queries. Rate limiting prevents abuse at volume.
Code Examples
// Example 1: Depth limiting with graphql-depth-limit
const depthLimit = require('graphql-depth-limit');
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [depthLimit(7)]
});
// Example 2: Query complexity analysis
const { createComplexityLimitRule } = require('graphql-validation-complexity');
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [
createComplexityLimitRule(1000, {
onCost: (cost) => console.log(`Query cost: ${cost}`)
})
]
});
// Example 3: Rate limiting plugin
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [
{
requestDidStart: async ({ request }) => {
const key = request.http?.headers.get('x-api-key');
const isAllowed = await rateLimiter.check(key, 100, 60);
if (!isAllowed) {
throw new Error('Rate limit exceeded');
}
}
}
]
});
Common Mistakes
- Setting depth limits too low, breaking legitimate queries with deep nesting
- Only limiting depth without cost analysis for expensive fields
- Using IP-based rate limiting for APIs that share IPs behind NAT
- Not applying security rules to subscriptions and Websocket connections
- Relying solely on client-side query limits
Practice
- Add depth limiting to your GraphQL server with a limit of 5.
- Implement query complexity analysis with a cost budget of 500.
- Add rate limiting based on API keys.
- Implement persisted queries for a production-ready setup.
- Challenge: Build a custom cost analysis function that assigns higher costs to database-heavy fields.
FAQ
Mini Project
Add three security layers to your library API: depth limiting (max 6), cost analysis (budget 1000), and rate limiting (100 requests per minute per API key). Test each layer with malicious queries.
What's Next
Next, you will build a complete GraphQL project that integrates all concepts from this section.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro