API 风格与实时性:GraphQL / tRPC / WebSocket 是什么
REST 够不够用、GraphQL 与 tRPC 各解决什么、三种实时方案一句话
- 知道 REST 什么时候够用、什么时候不够
- 能按 Richardson 成熟度模型判断自己的 API 是真 REST 还是 RPC 壳
- 理解 GraphQL「前端要什么给什么」与它的 N+1 坑
- 能说清轮询 / SSE / WebSocket 三种实时方案的选型直觉
REST = 点菜时一个菜一个菜喊,喊十次跑十趟;GraphQL = 一张图把整桌菜一次点齐;WebSocket = 直接打通电话,双方不用一句一句喊。不是谁高级,是哪种场景用哪种。
REST 够用吗?
大多数 CRUD 后台、博客、Todo 类应用,REST 完全够用:URL 表资源、方法表操作,简单、通用、易调试。但当一个页面要同时拼用户信息、订单、商品列表时,REST 往往要发好几个请求,前端还得自己拼数据。
REST 成熟度模型:你做到第几层了
Richardson 成熟度模型把 REST 分四级,用来判断你的 API 是真 REST,还是「披着 REST 皮的 RPC」。第 0 级:一个 URL 一个动作,像老式 RPC,比如 POST /api/getUser。第 1 级:URL 开始命名资源,比如 /api/users。第 2 级:用 HTTP 方法表动作,GET 查、POST 增、PUT 改、DELETE 删。第 3 级:再加 HATEOAS——响应里自带下一步能点的链接,最理想但实务里很少做到。
实务里做到第 2 级就是合格的 REST。很多团队嘴上说用 REST,其实停在第 0 级:URL 全是 /api/xxxAction,方法永远 POST,那只是借了 REST 的壳,调试、缓存、网关都没法按资源语义来。
0 级:一个 URL 一个动作(RPC 壳)。1 级:URL 命名资源(/api/users)。2 级:HTTP 方法表增删改查(合格线)。3 级:响应自带下一步链接(理想,少人做)。判断自己 API 的体检线:是不是每个动作都在 POST?是,就还在 0 级。
GraphQL:一次拿全
GraphQL 只有一个 /graphql 入口,前端在请求里写明自己要哪些字段,后端精确返回这些字段。前端要什么给什么,不多拿不少拿,解决了「多次请求 + 字段冗余」的问题。代价是后端要写类型系统、查询优化更复杂。
# REST:拿用户要发一次,拿他的文章再发一次
GET /api/users/1
GET /api/users/1/posts
# GraphQL:一个请求,写明要什么
# POST /graphql
# { user(id: 1) { name posts { title } } }GraphQL 的坑:N+1 与 DataLoader
GraphQL 灵活的代价是 N+1 查询问题:前端要「用户和他最近的 10 篇文章」,naive 实现是先查 1 个用户,再对每个用户循环查文章——一次请求打 11 次数据库。用户数放大到 100,就是 1 + 1000 次查询,数据库直接被自己的一个请求打挂。
解法是 DataLoader:把同一时刻里要查的所有用户 id 攒成一批,用一条 SQL where id in (...) 全拉回来,再分发给各个等待的 resolver。GraphQL 不是银弹,查询优化做不好,它比 REST 还慢——这也是中小项目没必要上 GraphQL 的现实原因。实务判断标准:你的前端是不是经常『一个页面拉七八个接口』?是且后端是单体,GraphQL 收益明显;接口就那几个的小项目,多写两个 REST 端点比搭一套 GraphQL 服务省事得多。
tRPC:端到端类型安全
tRPC 主要用在前后端都是 TypeScript 的项目里:后端写的函数,前端直接像调本地函数一样调,类型自动共享,接口改了前端编译期就报错。它解决的是「前后端接口字段对不上」这种靠口头约定的痛——那种 bug 要等到运行时才在浏览器里炸出来。它的代价是前后端最好在同一个 TypeScript 工程里共享类型包,一旦要同时服务 App、小程序等多端,类型共享会变麻烦,所以 tRPC 适合『一个仓库前后端全包』的项目。
三种实时方案一句话
轮询:前端每隔几秒问一次「有新的吗」,最简单但费请求。SSE(Server-Sent Events):服务端单向推送,适合消息通知、AI 流式输出。WebSocket:双向长连接,适合聊天室、协同编辑、实时对战。具体选型对比归 s15-m2-l2,这里先混个脸熟。
别小看轮询:在更新频率不高(比如每 30 秒看一次订单状态)的场景,轮询比上 WebSocket 省心得多——没有连接保活、没有重连状态机,坏了下次自动好。技术选型不是越实时越高级,而是用最小复杂度满足需求。一句话收尾:默认 REST,按需取数上 GraphQL,TS 单仓要类型上 tRPC,实时推送先想 SSE 够不够,双向互动才上 WebSocket。
SSE vs WebSocket 选型直觉
一句话直觉:只需要服务端往客户端推(消息通知、AI 打字机式流式输出、股票报价推送)用 SSE;需要双向频繁互动(聊天室、协同编辑、多人对战)用 WebSocket。SSE 是跑在 HTTP 上的长连接,自带断线自动重连、走标准 80/443 端口、对代理友好,缺点是只能服务端→客户端单向。
WebSocket 是独立的 ws:// 协议,一旦连上双方随便发,但你得自己维护两件事:心跳和重连。空闲时服务端要定期发 ping 包探活,不然中间代理早把这条空闲连接掐断了双方还不知道;连接断了前端要按指数退避重连(第一次 1 秒、第二次 2 秒……),防止疯狂重连把服务打死。
这一课不做技术选型对比,也不写选型矩阵——那是 s13-m3-l3 和 s15-m2-l2 的事。你只要记住:REST 是默认值,字段要按需拼上 GraphQL,TS 全家桶要类型安全上 tRPC,要实时再想轮询/SSE/WebSocket。
一个页面要同时拉用户资料、购物车、推荐列表,发好几个 REST 请求又慢又碎,优先考虑哪种风格?
做一个 AI 聊天产品,需要把模型逐字输出实时流给用户,用户不怎么往服务端发消息。优先用哪种实时方案?
资深工程师加餐
底层原理 · 大厂视角 · 工程经验,点卡片展开
网络侧:DNS→TCP/TLS→HTTP 请求与响应;浏览器侧:解析 HTML 建 DOM、解析 CSS 建 CSSOM、合并成渲染树,再经过布局(Layout)、绘制(Paint)、合成(Composite)上屏。前端性能优化几乎都围绕这条链路:减小资源体积、优化关键渲染路径、避免频繁触发重排重绘。
挑战任务
按场景选 API 风格
场景:你要给三个不同产品选接口风格。写 pick_style(scenario):含「实时/聊天/对战」返回 WebSocket,含「字段/一次拿全」返回 GraphQL,其余返回 REST——这就是最朴素的选型决策。
课后作业
给你的产品选实时方案
场景:你在做一个 AI 产品。请判断三个模块各用哪种方案并写一句话理由——①AI 逐字流式输出;②多人在线协作白板;③每隔几秒刷新的系统通知。