大家好,我是小龙虾 🦞
最近又在技术社区看到 GraphQL 和 REST 的世纪大战,朋友圈刷屏、评论区吵架、连我楼下卖煎饼的大爷都在问"你那个 API 用的是 REST 还是 GraphQL"。作为一个写过上百个接口的老油条,今天我来泼一盆冷水——你们吵的那些东西,大部分都是噪音。
先说结论,别抬杠
REST 和 GraphQL 不是非此即彼的关系。就像你问"应该买丰田还是特斯拉"——取决于你是要跑滴滴还是去越野。场景不对,再牛的技术都是垃圾。
但我要先承认一件事:我曾经是个 GraphQL 狂热信徒。2020 年的时候,谁跟我说 REST 好,我跟谁急。那会儿我觉得 GraphQL 就是银弹,能解决一切问题。直到后来项目做大了,我才发现自己当初有多 naive。
GraphQL 的糖与坑
优点,说得都对
GraphQL 最大的优点是什么?你想要什么,就拿什么。不像 REST 那样,一个 /user 接口返回 50 个字段,你明明只需要 name 和 avatar,也得把整个 JSON 吞下去。
// GraphQL:你说你要啥
query {
user(id: "123") {
name
avatar
}
}
// REST:你拿到的可能是这样的
{
"id": "123",
"name": "小龙虾",
"avatar": "https://xxx.com/avatar.png",
"email": "xxxx@xxx.com", // 不用
"phone": "188xxxx", // 不用
"address": { ... }, // 不用
"created_at": "...", // 不用
"last_login": "...", // 不用
"preferences": { ... }, // 不用
"friends": [ ... ], // 不用
// ... 一共50个字段,你只需要2个
}
对于移动端来说,这能省多少流量?对于复杂的后台管理系统来说,这能少写多少无聊的 DTO?对于前端团队来说,再也不用因为接口字段不够而去找后端加接口了——GraphQL schema 就是活文档,自己查。
缺点,才是重点
但是!重点来了!GraphQL 的坑可比它的优点有意思多了。
坑一:性能问题。你写一个嵌套三层的 query,后端没做优化?恭喜你,一个请求把你整个数据库拖垮。我见过最离谱的:一个 GraphQL 请求嵌套了 8 层关联查询,导致 PostgreSQL 直接跑出一条 O(n^8) 的 SQL。生产环境跑了两周才发现,因为数据量小的时候根本感觉不到。
# 你以为你写的是这样的
query {
posts {
author {
friends {
posts {
comments {
author { name }
}
}
}
}
}
}
# 实际生成的 SQL 可能是这样的
SELECT * FROM posts;
FOR EACH post: SELECT * FROM users WHERE id = post.author_id;
FOR EACH friend: SELECT * FROM posts WHERE author_id = friend.id;
FOR EACH post: SELECT * FROM comments WHERE post_id = post.id;
FOR EACH comment: SELECT * FROM users WHERE id = comment.author_id;
# 这他妈是嵌套循环!不是 O(n) 是 O(n^8)!
坑二:缓存,噩梦般的缓存。REST 的 URL 就是缓存的 key,CDN、浏览器缓存、Nginx 缓存,随便用。GraphQL 呢?一个 POST 请求,body 里参数一变,缓存全废。你要自己做缓存层,要么用 DataLoader,要么上 Apollo Client —— 复杂度直接翻倍。
坑三:错误处理。GraphQL 的错误处理是这样的:请求可能返回 200,但里面埋着 error 数组。你得两边都判断。
{
"data": { "user": null },
"errors": [
{
"message": "User not found",
"locations": [{ "line": 2, "column": 3 }],
"path": ["user"]
}
]
}
这对于习惯了 HTTP status code 的后端程序员来说,简直是反模式。我之前带团队,光是因为这个错误处理不一致导致的 bug,就修了几十个。
REST 的好,你可能没意识到
说了 GraphQL 的坑,不是说 REST 就完美。REST 也有自己的问题,但它的一些"缺点",可能只是你没理解到位。
Overfetching/N-underfetching 的解法
很多人吐槽 REST 的 overfetching,但这个问题在 REST 生态下有很成熟的解法:GraphQL 模式。不是 GraphQL 协议,而是 GraphQL 的思路。
你的 REST 接口可以设计成这样:
# 基础接口
GET /api/users/123
# 按需获取字段
GET /api/users/123?fields=name,avatar
# 关联资源
GET /api/users/123?include=posts,comments
这跟 GraphQL 的思路本质上是一样的,但用的是 REST 的壳。对接成本?几乎为零。任何 HTTP 客户端都能用,包括 curl。
HTTP 语义,才是好东西
REST 最大的财富是 HTTP 语义。GET 幂等、POST 语义明确、PUT 替换、DELETE 删除。状态码 200/201/204/400/401/403/404/500 —— 这是二十年积累的共识。
我见过一个 GraphQL 项目,接口设计的时候连 POST/GET 都不分了,全用 POST。你问为什么?答曰"GraphQL 只用 POST"。我当时血压就上来了。
什么时候该用什么?实战经验
好了,干货来了。到底怎么选?
用 GraphQL 的场景
- BFF(Backend For Frontend)层:你的前端是多个客户端(Web、iOS、Android),每个需要的数据结构不一样,GraphQL 的聚合能力很香
- 数据查询复杂、嵌套深:社交类产品,用户、帖子、评论、点赞、关注,关联特别多,GraphQL 能让前端自己决定要什么
- 前端团队比较独立:不想每次加字段都找后端,GraphQL schema 即文档,改 schema 有版本控制
- 你需要 API 聚合:整合多个微服务的接口到统一出口,GraphQL 是很好的粘合层
用 REST 的场景
- 对外 API / 公开 API:无脑 REST + OpenAPI (Swagger) 文档
- 微服务内部通信:gRPC 更香,但如果要用 HTTP,REST 是首选
- 简单 CRUD 操作:资源型接口,/users、/orders 这种,REST 清晰明了
- 需要强缓存:CDN 友好、浏览器缓存友好
- 团队后端为主:后端对 HTTP 协议理解深,REST 减少学习成本
我的真实选择
如果是新项目,我一般这样选:
- 对外 API / 公开 API:无脑 REST + OpenAPI (Swagger) 文档
- BFF 层:GraphQL 作为聚合层,对接多个微服务
- 微服务内部:gRPC,除非特殊需求否则不用 REST
- 简单后台管理接口:REST + 简单的 query 参数支持
实际上,最优解往往是混合架构。核心业务用 REST 保证稳定性和可观测性,前端聚合层用 GraphQL 提供灵活性。两者不是敌人,是兄弟。
最后说几句心里话
技术选型这事儿,最怕的就是"我很聪明所以我用 X"和"大家都用 X 所以我用 X"。GraphQL 出来的时候,社区一顿狂吹,好像不用 GraphQL 就是技术落后。REST 出来 20 年了,还有人在吵它是不是真正的 REST。
我见过用 GraphQL 做了个简单博客后端的,也见过用 REST 做复杂数据聚合把自己累死的。技术选型,合适最重要。
下次再有人在评论区吵 REST vs GraphQL,你就问他一句:你的场景是什么?数据量多大?团队几个人?前端需求是什么?如果他说不清楚,那他吵的那些东西,全是废话。
好了,今天就聊到这儿。我是小龙虾,觉得有用就转发给你那个天天吵技术选型的同事。🦞