限制 GraphQL 查询深度的正确方式
I’m happy to translate the article for you, but I need the full text you’d like translated. Could you please paste the content (excluding the source line you’ve already provided) here? Once I have the article text, I’ll translate it into Simplified Chinese while preserving the formatting, markdown, and code blocks.
介绍 graphql‑safe‑depth
GraphQL 功能强大、灵活且表达力丰富——但如果查询没有得到适当约束,这种灵活性可能会成为隐患。
过深的查询会导致解析器执行过多、内存使用过高,甚至出现拒绝服务(DoS)场景。
在本文中,你将了解:
- 为什么深层 GraphQL 查询是一个真实的问题
- 为什么许多现有的深度限制方案效果不佳
- graphql‑safe‑depth 如何以不同的方式解决该问题
- 如何在 Apollo Server 和 NestJS 中使用它
- 何时以及如何将其与其他安全措施相结合
🚨 问题:深层 GraphQL 查询
query {
user {
posts {
comments {
author {
profile {
avatar {
url
}
}
}
}
}
}
}
乍一看这看似无害,但它可能会:
- 触发 N+1 查询爆炸
- 消耗大量 CPU 和内存
- 成为 DoS 向量,无论是有意还是无意
GraphQL 并未默认设置任何深度或复杂度限制。
🤔 为什么现有解决方案不足
| 问题 | 描述 |
|---|---|
| ❌ 统计字段数量而非执行深度 | 一些库统计字段的总数,而不是最深的执行路径,这会导致误报或令人困惑的行为。 |
| ❌ 破坏自省 | 自省查询(__schema、__type、__typename)本质上往往很深,不应被阻止。 |
| ❌ 难以推理 | 某些实现难以定制、调试或向团队解释。 |
✅ 方法:graphql‑safe‑depth
graphql‑safe‑depth 是一个轻量级的 GraphQL 验证规则,专注于一件事:
- 🧠 测量最深的解析器路径
- 🔍 默认忽略 introspection 字段
- 🧩 完全支持 fragments(片段)
- ⚡ 零运行时依赖
- 🛠 TypeScript 为先,兼容 JavaScript
工作原理
- 该库挂钩到 GraphQL 的 validation phase(验证阶段)。
- 它遍历查询的 AST(抽象语法树)。
- 根据嵌套的字段选择计算深度。
- 记录最大执行深度。
- 如果深度超过
maxDepth,查询将在 执行前 被拒绝。
深度计算示例
✅ 有效查询(depth = 3)
query {
user {
profile {
name
}
}
}
❌ 无效查询(depth = 4)
query {
user {
profile {
address {
city
}
}
}
}
只有最深的执行路径重要——而不是字段的总数。
🚀 使用示例
Apollo Server (Node.js)
import { ApolloServer } from "apollo-server";
import { createDepthLimitRule } from "graphql-safe-depth";
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [
createDepthLimitRule({ maxDepth: 3 }),
],
});
Apollo Server (NestJS)
import { createDepthLimitRule } from "graphql-safe-depth";
GraphQLModule.forRoot({
autoSchemaFile: true,
validationRules: [
createDepthLimitRule({ maxDepth: 3 }),
],
});
⚙️ 配置选项
createDepthLimitRule({
maxDepth: number; // required
ignoreIntrospection?: boolean; // default: true
message?: (depth: number, maxDepth: number) => string; // optional
});
-
maxDepth – 允许的最大执行深度。
createDepthLimitRule({ maxDepth: 3 }); -
ignoreIntrospection – 为
true时,忽略 introspection(自省)字段。createDepthLimitRule({ maxDepth: 3, ignoreIntrospection: false, }); -
message – 自定义验证错误信息。
createDepthLimitRule({ maxDepth: 3, message: (depth, max) => `Query depth ${depth} exceeds the allowed maximum of ${max}`, });
🔐 安全考虑
深度限制 不是 万能钥匙,但它与以下措施配合良好:
- ✅ 查询复杂度限制
- ✅ 正确的身份验证和授权
- ✅ 限流
- ✅ 缓存和批处理(例如 DataLoader)
graphql‑safe‑depth 专注于做好一件事——以可预测的方式防止危险的深层查询。
📦 安装
npm i graphql-safe-depth
🔗 链接
- GitHub 仓库:
- npm 包:
🧠 最后思考
该库最初是作为学习练习而创建的,现已发展为具备稳定 v1.0.0 发行版的生产就绪工具。如果您在生产环境中运行 GraphQL 并且需要一个简单、可预测的深度限制,graphql‑safe‑depth 可能是一个合适的选择。
欢迎提供反馈、提交问题和贡献代码! 🙌