拆分你的数据与应用API:更进一步

Carson Gross

摘要: 如果你将API拆分为数据和应用API,如此处所倡导的,你应该考虑将应用API从JSON改为超媒体(HTML)并使用像htmx这样的超媒体导向库,以获得超媒体模型的优势(简单性、可靠性、灵活性等)。

问题

最近,Max Chernyak写了一篇题为《不要构建通用API来支持你自己的前端》的文章。他的摘要如下:

除非你在大公司工作且使用联合前端或GraphQL,否则你并不需要它(YAGNI原则)。

然后他讨论了通用API和应用API的不同需求。他列出了通用API的以下需求:

  1. 如何预测并支持所有可能的工作流程
  2. 如何避免尴尬工作流程中的N+1请求问题
  3. 如何测试每个可能请求的功能、性能和安全性
  4. 如何更改API而不破坏现有工作流程
  5. 如何在内部需求和社区需求之间优先考虑API变更
  6. 如何记录所有内容以使各方都能完成任务

而应用API的需求则是:

  1. 如何收集渲染页面所需的所有数据
  2. 如何优化对多个端点的请求
  3. 如何避免以非预期方式使用API数据字段
  4. 如何权衡新功能收益与新API请求成本

我将这种需求不匹配称为数据/应用API阻抗不匹配问题。

Max的建议是将API分成两个"部分":通用API和应用API:

我建议你停止将前端视为某种通用API客户端,开始将其视为应用的一半。

想象一下,如果你能直接发送整个"页面"所需的JSON。为/page/a创建一个端点,并在那里渲染整个/page/a的JSON。为每个页面都这样做。不要强迫前端开发者发送大量独立请求来渲染复杂页面。停止用人为限制来烦扰他们。保持一致。

对问题的正确认识

我完全同意Max对问题的分析。

我特别想强调的是,通用API需要保持稳定,而应用API必须快速变化以满足应用需求。

Jean-Jacques Dubray在这篇文章中描述了API设计者面临的困境:

如今我工作中最糟糕的部分是为前端开发者设计API。对话总是不可避免地变成:

开发者 - 这个屏幕需要数据元素x,y,z...能否请你创建一个响应格式为{x: , y:, z: }的API?

我 - 好的

这完美概括了Max注意到的矛盾:API工程师希望设计通用、稳定的API,但受制于快速变化的UI及其复杂的数据需求,而这些需求通常最好在服务器端解决。

正如Max指出的:

你可以保持"页面a"只做它需要做的事。你对"页面a"进行彻底的错误、安全、性能测试。你甚至可以通过一个大型SQL查询获取"页面a"所需的所有数据。

对解决方案的误解

因此,我完全同意Max关于存在数据/应用API阻抗不匹配问题的观点,并赞赏他建议通过将两者拆分为独立的关注点来解决这个问题,而不是转向GraphQL等解决方案。

然而,还有一个后续步骤

一旦你将应用API与通用数据API分离,你就不再受公共数据API约束的限制,可以自由地重新考虑该应用API的整体形式。我们可以随心所欲地设计它,所以让我们在思考上更开阔一些。

请注意,应用API的核心问题是快速变化和页面(或资源)特定调整。事实证明,我们有一种非常适合解决这个确切问题的技术:超媒体

通过HATEOAS实现的超媒体使得API变更不再是那么大的问题。当你改变超媒体API的形式时,这没问题:新的API只需反映在服务器返回的新的HTML中。你可以添加和修改端点,看吧(在近似情况下),你的客户端(即浏览器)不需要更新。

浏览器只是看到新的HTML,而操作它们的人类会适当地对新功能做出反应

因此,虽然我认为Max的思路是正确的,但我也认为他做得还不够:一旦你在思想上迈出了通过拆分关注点来解决数据/应用API阻抗不匹配问题的第一步,再往前走一小步就能重新发现超媒体的优势。

你可能会反对说:"哦,但是超媒体应用不太好用,我们不想回到Web 1.0时代。"

这是一个完全合理的反对意见,但人们一直在解决这个问题,现在有许多库可以在超媒体模型内解决HTML的可用性问题。

我最喜欢的两个是unpoly,当然还有我自己的htmx

总结

如果你改用超媒体应用API(实际上就是"像以前一样使用HTML"),那么你将获得REST-ful Web模型的所有好处(简单性、可靠性等),以及成熟Web框架中服务器端渲染的优势(缓存、SQL优化等)。

而且,通过选择像htmx这样的超媒体导向前端技术,你可以在该模型内创建出色的用户体验

旧事物再次焕然一新,但这一次,稍微好了一点。

</>