比较 REST、JSON:API 和 GraphQL

怀齐智
2023-12-01

这篇算是翻译、摘录和转写,原文:Headless CMS: REST vs JSON:API vs GraphQL,是Drupal项目的创始人Dries Buytaert和他同事一起在2019初写的,有很多跟Drupal相关的元素,但是质量很高,极具参考价值,所以我把其中跟Drupal相关的大部分去掉,然后把架构相关的摘录、翻译过来,并加入一些我自己的关于PythonDjangoFlask开发接口的经验,形成这篇文章。

首先,这篇文章是从请求效率、运维的简单性、API 可发现性、数据写入等来比较 REST、JSON:API 和 GraphQL,其中这里的 REST 就是指 RESTful 风格的 HTTP API 接口,一个接口一个接口写出来的那种,而不是类似Django-REST-Framwork那种设计和规范的框架做出来的。

然后定义比较的指标:

  1. 请求效率:能否在一次请求中获取所有需要的数据对性能至关重要。请求和响应的大小都影响效率。
  2. 文档,API 可探索性和Scheme: API 应该易于理解且易于发现。
  3. 运维的简单性:应易于安装、配置、运行、扩展和安全。
  4. 写入数据:写入不应该比读取复杂得多。

作者做了个表来比对:

RESTJSON:APIGraphQL
请求效率比较差;需要多个请求完成需求,而且响应臃肿。出色的;可以定制仅返回需要的内容。出色的;响应仅包括所要求的内容。
文档,可探索性和Scheme较差的;没有Scheme,不可探索。可接受。出色的;有精确的Scheme,还有优秀的工具。
操作简单可接受。出色的;不需要客户端库,但有许多而且可用。较差的;客户端库是必需品。
写入数据可接受;HTTP语义具有指导意义,每个请求一次写入。出色的;每个请求一次写入,但规范中正在加入关于多次写入的内容。较差的;可以在单个请求中多次写入。

所以Dries哥态度是很明确的,他选JSON:API。接下来从这4个方面分别展开说。

请求效率

这里其实分了两部分内容,一部分是类似外键或引用的处理方式,另一部分是对响应体的裁剪能力。

我们先来看第一部分,Dries 哥举了一个例子,比如你要获取/article/42/article/72,用 REST 时需要请求两次,如果要获取它们的作者信息(假定是通过外键存在作者表里,一般也是这样的),那么还需要再执行两次/author/3/author/7,而且还是先获取文章,然后解析得到作者ID,再获取作者信息,这样就请求很多,而且是串行的,所以体验就是很慢的。

而 JSON:API 和 GraphQL 都可以一次请求获取多篇文章和对应的引用(外键)信息,所以Dries哥说他们要高效得多。

然后是有些字体客户端的这一次请求可能用不上,那么就需要客户端可以自主地裁剪响应体的返回,这样返回的数据少了,传输和处理的时间也少了,体验就会更快。这方面业务人员一个一个编写的 REST 接口基本上不会提供这样的能力,总是会返回全部数据,烦人而且低效。GraphQL 的方法是客户端开发者显式地声明自己要的字段,可以达到裁剪响应体的目的,但会把整个查询搞得很大,导致缓存失效。而JSON:API用稀疏字段集和目标字段列表解决了这个问题,更灵活,而且缓存友好。

文档、API可探索性和Scheme

这个是指怎么样发现和理解API:可用的资源类型、每个资源的字段、它们之间的关系等。而且,如果这字段是日期或时间,指定的日期或时间是什么机器可读格式?没有这些,客户端开发者是很难对接API的。有没有自动生成的文档来降低API开发者的工作量?

这个领域的赢家是 GraphQL,它有很丰富的工具来做到这一点。但JSON:API也不错,通过浏览器可以从一个资源跳转到另一个资源,用 cURL 或 Postman 之类的工具可以调试和尝试。

运维的简单性

这方面Dries哥主要是比较了如何与CDN、反向代理等集成方面的事项。这个指标赢家是JSON:API,GraphQL失败在缓存友好性,部署失败等方面。

写入数据

对于大多数 REST API 和 JSON:API,写入数据就像获取数据一样简单:如果您可以读取信息,那么您能猜到如何写入,但 GraphQL需要为每个写操作编写代码,而且GraphQL 并没有规定对资源的写入操作的单一方式,因此有许多相互冲突的最佳实践。本质上的原因是GraphQL 规范针对读取而不是写入进行优化。

不过GraphQL 规范自动支持您已经实现的突变的批量/批处理操作,而 JSON:API 规范需要到1.2版本才支持。

最后

所以Dries哥的心水之选是不言自明的JSON:API,所以他说:

对于 Drupal 核心的需求,我们将 JSON:API 排在 GraphQL 之上,将 GraphQL 排在 REST 之上。因此,Drupal 8 核心只添加 JSON:API 而不是将 JSON:API 和 GraphQL 都添加进来。

我的话

关于REST,作为用过Django-REST-Framework用户,我觉得REST并没有如此不堪,只要有精心设计、并遵循规范,再补上文档自动生成等基础设施,像DRF这样,其实非常漂亮。不过Dries哥在行文之初就把这种REST排除在外了。我也能理解,毕竟再造DRF这样的轮子成本太高了,遵循类似JSON:API这样的规范优势非常大,我自己也在项目中选择JSON:API,现在用的是flask-rest-jsonapi,欢迎多交流。

 类似资料: