01 它是干嘛的
GraphQL 是一套让客户端「按需点菜」的数据查询方式。它把过去需要很多个不同地址的接口,收拢成唯一一个入口:客户端在一份声明里写清楚自己需要哪些字段,服务端照着这份声明把数据取好返回。它解决的是接口太多、数据要得太多或太少的尴尬。
02 为什么会有它
在 GraphQL 之前:一个页面要打十几个接口,还常常拿回一堆用不上的数据
过去前后端约定接口用的是 REST 风格:每种数据各有一个地址,要用户信息问一个地址,要他的文章再问另一个地址。结果手机端打开一个页面,可能要连着发十几个请求,把几份数据拼起来才能显示,网络一慢,页面就一直在转圈。
更麻烦的是「要多了或要少了」。同一个接口服务着手机和网页,手机只想要姓名和头像,接口却把整个用户档案全返回,白白浪费流量;反过来当页面临时需要一个新字段,后端不改接口就取不到,前端只能干等。这种尴尬在移动网络下尤其致命。
Meta 在 2012 年前后为自家移动应用造了 GraphQL 来治这个病:把接口收成一个总入口,客户端像填点菜单一样写明要哪些字段,服务端照着单子取。2015 年它被开源,随后成为很多公司在 REST 之外的另一种主流选择。
03 它怎么工作
客户端不再记一堆接口地址,只对着唯一端点提交一份结构化的声明,服务端解析这份声明,按字段逐一取数,再把形状完全对上的数据返回。
一个入口、一份声明、一次返回:请求是怎么被满足的
- 1① 客户端写下自己的需求
用一段和返回结果长得很像的声明文字,列出自己要哪些字段,甚至能嵌套到关联数据里。
- 2② 请求发往唯一入口
所有需求都发向同一个地址,不再需要记十几个不同的接口路径。
- 3③ 服务端校验声明
服务端先按预先定义好的接口说明书检查:这些字段存在吗、类型对不对,不合格直接拒绝。
- 4④ 逐字段取数
每个字段对应一段取数逻辑,框架按声明依次执行,需要关联数据时再去取关联部分。
- 5⑤ 按声明的形状返回
返回结果的结构与客户端声明完全一致,不多一个字段也不少一个,前端拿到即可直接渲染。
04 谁在用它
手机流量宝贵,一次只取需要的字段,少发请求也少传数据,页面更快打开。
前端面对一个入口,背后由它去汇总多个服务的数据,前端不必关心数据来自哪里。
页面临时要加个字段,前端改一改声明即可,不必等后端专门开新接口。
网页、手机、手表各自的界面不同,用同一入口各取所需,服务端不用为每端单独定制。
05 怎么用
门槛中等:看懂声明语法很快,真正的工作量在服务端那一侧的定义与实现。
- 01先由服务端定义接口说明书,写清有哪些数据类型、每个类型有哪些字段。
- 02为每个字段实现取数逻辑,明确它是从数据库取还是调用其他服务。
- 03搭好一个统一入口,让所有查询都发往这里。
- 04前端按说明书写声明,把需要的字段列出来,提交请求。
- 05在开发工具里试跑声明,返回结构与你写的一一对应,调试很直观。
- 06上线前给查询加上复杂度与频率限制,防止有人写下过深的嵌套把服务拖垮。
# 服务端照着这份声明返回,结构一模一样,不多也不少
query {
user(id: "42") {
name
avatar
posts(last: 3) {
title
}
}
}避坑提示
- !避免层层嵌套地索取关联数据,嵌套太深会让服务端压力陡增,必要时拆成几次查询。
- !缓存比 REST 更难做:所有请求都走同一个地址,浏览器与 CDN 的默认缓存基本失效,需要额外方案。
- !要让服务端对查询的复杂度做限制,否则一句精心构造的深嵌套声明就可能拖垮数据库。
06 关键概念
- 说明书(Schema)
- 服务端对外公布的一份合同,写明有哪些数据、每个数据有哪些字段、什么类型。
- 解析器(Resolver)
- 每个字段背后真正去取数的那段代码,负责把数据从数据库或别的服务里拿出来。
- 过度获取
- 接口返回了客户端根本用不上的字段,白白浪费流量与时间。
- 单端点
- 所有请求都发往同一个地址,靠请求内容区分要什么,而不是靠不同的网址。