为什么不是GraphQL呢?REST API更胜一筹,但GraphQL仍胜出

2020-11-06 18:08:51

我认为GraphQL将改变世界。在未来,您可以使用GraphQL查询世界上的任何系统。我正在建设这个未来。那么,我为什么要反对使用GraphQL呢?我个人最恼火的是,当社区一直在宣传GraphQL的好处时,这些好处是非常通用的,实际上与GraphQL没有任何关系。如果我们想要推动领养,我们应该诚实,摘下玫瑰色的眼镜。本文是对凯尔·施雷德(https://www.apollographql.com/blog/why-use-graphql/).)的《为什么使用图形》的回应。这不是直接的批评。这篇文章只是一个很好的基础,因为它代表了我在社区里经常听到的观点。如果你读了整篇文章,你会花一些时间,你会完全理解为什么我认为凯尔的文章应该取名为“为什么使用阿波罗”。

如果你还没有读过凯尔斯的文章,我想你最好先读一读:https://www.apollographql.com/blog/why-use-graphql/。

作者指出,REST API有一组缺点,以及GraphQL如何解决所有这些缺点:超取对多个资源的多个请求对嵌套数据的瀑布网络请求每个客户端都需要知道每个服务的位置。

前三个问题可以通过编写另一个REST API作为特定用户界面的外观来解决。以Next.JS为例。Next允许您使用非常轻量级的语法定义API。无需从客户端发出多个请求,您可以将这些调用包装到一个API中,并使其成为服务器端调用。这种方法也可以解决过度和低取问题,因为您可以在将数据发送回客户端之前对其进行操作。描述的模式被命名为前端后端(Bff)。它并不局限于像Next.JS这样的全栈框架。你也可以为你的移动应用程序构建一个BFF。

使用BFF模式,客户端本身不必知道每个服务的位置。但是,实现BFF的开发人员需要了解服务场景。希望您的所有服务都有Open API规范,这些规范很好地呈现在开发人员门户中。如果是这样的话,写一篇好朋友应该很容易。

使用GraphQL,仍然需要一个实现解析器的开发人员。实现解析器与构建BFF的任务大同小异,逻辑非常相似。那么,真正的区别是什么呢?

BFF更容易实现,因为有更多可用的工具。例如,如果你将Next.JS这样的框架与SWR钩子(在重新验证时失效)结合使用,你会得到带有eTag的自动缓存和开箱即用的缓存失效。这减少了服务器和客户端之间发送的数据量。它的数据甚至比GraphQL更少,因为您没有发送查询有效负载,如果响应仍然有效,服务器将返回304(未修改)。此外,你不必使用像阿波罗这样的重量级客户。Vercel的SWR库很小,非常容易使用。它附带了对分页、挂钩的支持,并有助于非常高效地来回导航。

GraphQL已经持久化了查询,但实现这一点会带来额外的开销。如果你不使用像Relay这样的客户端,它在默认情况下保存查询,你必须自己做,或者使用一些第三方库来实现它。与使用例如Next.JS的BFF方法相比,在前端获得相同的结果要复杂得多。您将如何使用GraphQL实现eTag?如果没有任何更改,如何让GraphQL服务器返回304个状态代码?难道您不需要先将所有查询转换为GET请求吗?如果是这样的话,你的GraphQL客户端和服务器容易支持吗?

在用户体验和开发方便性方面,BFF显然是赢家。客户端和服务器之间的数据传输更少。更易于实施。客户更小,活动部件更少。

但这其中有一个问题。你必须为每个单独的前端建立一个好朋友。如果你有很多这样的人,这可能会是一项很大的工作。你必须维护好所有的好朋友。你必须操作它们。你必须确保他们的安全。

如果你可以在不做权衡的情况下同时拥有两者的好处,那不是很好吗?这正是WunderGraph的用武之地。一个使用GraphQL构建BEF的框架。

在下一段中,Kyle将继续讨论与版本化API相关的问题。他说的完全正确,一个API的版本太多,很难跟踪。然后他得出结论,在GraphQL中,图形只有一个版本,并且可以在模式注册表(Apollo的付费功能)中跟踪更改。出于这个原因,你在版本控制方面不会有任何问题,他说。

我很难得出同样的结论。仅仅因为GraphQL模式本身不支持版本控制并不意味着问题就消失了。如果您只是不对RESTAPI进行版本化,则会得到同样的效果。事实上,许多专家说,如果没有必要,您应该始终尝试不引入API的版本。话虽如此,是什么阻止您运行两个版本的GraphQL模式呢?虽然我不认为这是个好主意,但这在技术上是可行的。

如果您的组织中存在RESTAPI版本过多的问题,那么在使用像GraphQL这样的新工具来解决这个问题之前,也许您应该先了解一下组织。有这么多版本的原因是什么?也许流程的改变或新的团队结构可能会有所帮助?GraphQL完全无法解决您的版本控制问题。相反,我认为这实际上让情况变得更糟。

您是否必须支持移动应用程序?你应该意识到,发布本机应用程序需要时间。你必须等待应用商店的批准,你可以预计你的许多用户永远不会(或缓慢地)安装新版本。如果您希望在不中断客户端的情况下在此场景中引入突破性更改,该怎么办?这是不可能的。你必须以一种不间断的方式引入这一变化。如果能从Facebook那里听到他们是如何避免击溃客户的,那将是一件很有趣的事情。

在使用GraphQL的情况下发展您的模式意味着,您不建议使用旧的字段,而添加一个新的字段。新客户使用新字段,而您希望使用旧字段的客户数量会越来越少。希望您有一个系统,可以强制您的用户在某个时间点下载一个新版本。否则,您可能会被迫无限期地支持已弃用的字段。如果是这样的话,GraphQL的弃用模式对你一点帮助都没有。

使用REST,您可以创建新的终结点或现有终结点的另一个版本。问题是一样的,解决方案只是看起来有点不同。

明确地说,如果你不能控制你的客户端,你真的需要某种版本控制。如果你只有一个Web应用程序,你就不需要这个功能了。但话又说回来,GraphQL可能也是矫枉过正。

作者的实际意思是什么?我非常肯定他意识到了部分反应。我猜他想说的是,有人需要实施部分回应。实际上,当您从资源中选择子域时,GraphQL看起来非常熟悉。有了GraphQL,我们就有了这个开箱即用的功能。

另一方面,有了好朋友的方法,你就不需要这个了。只需准确返回您需要的数据即可。同样,像Next.JS这样的全堆栈框架使实现这一点变得更简单,使缓存更容易,并免费为您提供基于ETag的缓存失效。

综上所述,GraphQL为您提供了所需的数据。部分响应也能达到同样的效果。BFF会带来额外的实施和维护成本,但拥有更好的用户体验和DX。

在这一段中,Kyle解决了RESTAPI类型不严格的问题。他谈到了API的问题,不清楚你是得到了一组帖子还是其他东西,以及查询参数如何使情况变得复杂。他还指出,由于其严格的类型系统,GraphQL没有这个问题。

我认为凯尔所说的是一个组织问题,你需要一个组织解决方案。

当您允许开发人员在不发布Open API规范(OAS)或类似规范的情况下部署RESTAPI时,您会遇到他所描述的那种问题。使用OAS可以非常容易地描述所有资源。OAS还允许您描述OAuth2流和每个端点所需的作用域。此外,您还可以描述查询参数的确切类型和验证规则,这是GraphQL所缺少的特性。

看看GraphQL,就无法描述身份验证、授权和输入验证。GraphQL缺乏这些功能,因为Facebook的发明者在不同的层面解决了这个问题。他们没有必要将这些功能添加到GraphQL中。您可以将自定义指令添加到您的模式中,以获得类似于OAS的结果,但这将是一个自定义实现,您必须自己进行维护。

您可能认为OAS并不保证API的响应符合规范。你说得对。但是,GraphQL模式如何保证什么呢?

GraphQL自检是将特定的GraphQL查询发送到服务器以获取有关GraphQL架构的信息的行为。GraphQL服务器可以自由地回答它想要的任何类型。如果您发送查询,服务器可以使用不符合自检响应中的GraphQL架构的响应进行响应。以阿波罗联盟为例。您将模式上载到模式注册表中,然后错误地部署了错误版本的GraphQL服务器。如果更改字段的类型,客户端可能会感到困惑。

当我们谈论GraphQL中的类型安全时,我们实际上的意思是,我们信任GraphQL服务器的行为与自省查询响应所宣传的完全一样。为什么我们不能以同样的方式信任开放API规范?我想我们可以的。如果我们不这样做,我们就会遇到人的问题,而不是技术问题。

下一小段是关于GraphQL如何提高客户端性能和减少网络往返。

我想我已经足够解释了,与重量级的GraphQL客户端相比,BFF的功能要强大得多,而且你从“过时的同时重新验证模式”中获得了多少好处。

也就是说,GraphQL确实减少了请求数量,并减少了整体数据传输。然而,您应该始终考虑在您的前端添加GraphQL客户端的成本。

下一节将介绍如何将OAS等工具用于RESTful API开发,以及在微服务环境中维护多个OAS所面临的挑战。Kyle将单个GraphQL模式与分散在多个Git存储库中的Swagger文件进行了比较。

我认为很明显,浏览单个GraphQL模式要比查看多个OAS文件(位于git存储库中)简单得多。然而,公平地说,我们必须将苹果与苹果进行比较。如果你想让你的开发人员更有效率,你就不会把OAS文件放到GIT存储库中,然后就到此为止了。您将运行一个开发人员门户,您可以在其中搜索API并在它们之间导航。

OAS依赖于JSON-Schema,它具有一个令人惊叹的特性:您可以从另一个文档引用对象类型。如果需要,您可以将OAS划分为多个相互引用的文件。还有一种工具可以将多个OAS文件合并成一个OAS文档。然后,您可以使用该文档并将其提供给开发人员门户,该门户允许您将所有API作为一个整体进行探索。请记住,设置所有这些都需要额外的成本。您需要运行一个开发门户或购买一个。您必须描述所有API,至少在开始时,这可能是一种负担。

还有一件事要补充,有很多框架可以让你用你最喜欢的编程语言来描述模式,比如通过定义对象或类。然后,您将获得一个自动生成的Open API规范,该规范在一个众所周知的端点上提供服务。

让我们将其与GraphQL进行比较。基本上有两种方法,SDL优先和代码优先。无论您走哪条路,最终都会得到一个GraphQL模式,它描述了您的所有类型和字段,并允许您对它们进行注释。

那么,有什么不同呢?OAS带来了更多的设置开销。另一方面,OAS内置了记录示例用例、身份验证和授权以及输入验证规则的支持。

请记住,GraphiQL本身没有多个GraphQL模式的概念。如果你有多个GraphQL(微)服务,你必须运行或购买一个专用组件,例如一个模式注册表,它类似于RESTAPI的开发者门户。

有一件事我想多写一段,那就是API用例。仅仅因为您有一个OAS或GraphQL模式并不意味着您的API有很好的文档记录。API用户可以使用API做什么?他们怎么能用呢?哪些是好的用例?不好的是什么?去哪里寻求帮助?如何对用户进行身份验证?我需要API密钥吗?以一种帮助API使用者使用的方式记录API比向类型和字段添加描述要多得多。OAS允许您添加示例有效负载并对其进行描述。GraphQL缺少此功能。看一看斯利普,就能看到这方面的积极例子。它们远远超出了Swagger或GraphQL Playround所能达到的水平。

另一方面,如果您查看GitHub的公共GraphQL API,您将发现没有一个查询示例。假设您想要获取有关存储库及其所有问题的信息。您必须打开GraphiQL并开始搜索。然而,GraphiQL中的搜索功能并没有真正对您有多大帮助。需要有人坐下来编写如何使用API的示例查询和用例。否则,它真的很难开始。

因此,尽管社区一直在说GraphQL是自我记录的,但仅有这一功能并不能构成有用的API。OAS为您提供了添加用例的工具,但您仍然需要编写用例。无论您选择什么工具或语言,让API对他人有用都是需要付出努力的。

在这一段中,作者说,保留旧版本的RESTAPI用于移动应用程序是一件痛苦的事情。他的结论是,因为我们只使用一台GraphQL服务器,所以不存在这个问题。

我很抱歉,但再一次,我得出了一个完全不同的结论。如果将规则设置为不允许版本控制,则可以添加新终结点或交换现有终结点的实现。在这种情况下,GraphQL和REST之间没有区别。对于REST和GraphQL API来说,支持传统应用都是一个挑战。您需要找到一种不破坏客户端和服务器之间的契约的方法。不管你的服务器公开的是REST还是GraphQL,问题都是一样的。

在错误处理方面,作者描述了这样一种场景:客户端必须进行3次后续REST API调用,而不是使用部分数据响应单个GraphQL查询。

使用GraphQL,解析部分数据的逻辑驻留在服务器中。客户端需要有额外的逻辑来适当地对部分响应做出反应。

使用REST,获取部分数据的逻辑可以位于客户端或BFF中。无论哪种方式,其逻辑都与GraphQL大致相同,它只是位于另一个地方。显然,rest API用例还需要客户端中的逻辑来处理部分响应。此逻辑与GraphQL用例中的逻辑几乎相同。

没有什么可以阻止您在REST响应中返回某些失败原因的特定信息。OAS允许联合类型,因此您可以自由地向客户端提供有关部分响应的丰富信息。这类似于Sascha Solomon(https://sachee.medium.com/200-ok-error-handling-in-graphql-7ec869aec9bc).)描述的响应联合的概念。

GraphQL真的有更好的错误处理吗?我认为OAS和GraphQL都为您提供了以非常友好的方式处理错误的好工具。这取决于开发人员如何充分利用这些工具。天下没有免费的午餐。

Kyle在总结整篇文章时说,GraphQL是API的未来,因为它在性能、负载大小、开发人员时间和内置文档方面都具有优势。

更好的性能和更小的负载大小并不是GraphQL独有的特性。您可以使用其他工具获得更好的结果,或者必须扩展GraphQL,例如使用Relay获取持久化查询。要从GraphQL文档中获得真正的好处,您肯定需要做更多的工作,而不仅仅是向您的模式添加描述。

一旦尘埃落定,炒作烟消云散,我们就必须看清事实。我们不应该试图让世界相信GraphQL不是什么东西。使用GraphQL有很多好处,但根据你的用例,你可能不会真正从中受益。与其把GraphQL宣传为圣杯,我们应该给出一个更微妙的回应。

解决这个问题的最好方法是先看问题,然后对解决问题的可能工具进行独特的比较。如果您的组织在实现REST API方面失败,GraphQL如何解决这个问题?也许你的组织内部有什么需要改变的?另一方面,如果这不是组织上的问题,而且你完全确定REST不是你的用例的好选择,我打赌你会喜欢GraphQL的开发者体验。

GraphQL本身几乎没有什么用处。正是这些工具让GraphQL变得如此强大。这是社区,是我们!是阿波罗、Hasura、The Guild、FaunaDB、Dgraph、GraphCMS和我希望WunderGraph这样的公司让GraphQL变得如此强大。它是GraphiQL,各种GraphQL客户端,模式拼接,联合。这个工具的整个生态系统是GraphQL成为下一个大事件的原因。

更多的工具和服务将加强生态系统。更强大的生态系统将带来更多的采用,这将再次吸引更多的公司将服务和工具添加到GraphQL生态系统中,这是一个非常积极的循环。

在这一点上,GraphQL与Kubernetes非常相似。容器运行时Docker是不够的。将复杂的Syscall封装到一个简单易用的API中是可行的,但为了创建一个丰富的生态系统,需要一个具有足够表现力并允许非常容易扩展的调度器。

GraphQL为我们提供了一种定义和使用API的语言,与Docker一样简单。如果您以前见过Javascript和JSON的几行代码,GraphQL会立刻感到熟悉。但与Docker相似的是,该语言本身并没有那么强大。它是可扩展性和围绕它的工具。

Facebook开源的语言并不是它成功的原因。正是像Relay这样的工具做到了这一点。不幸的是,许多内部使用的工具从未公之于众。社区必须跟上这一点,我认为我们已经取得了相当大的进展。

当凯尔问“为什么是GraphQL”时,我想他真正的意思是“为什么是阿波罗”。答案很简单。没有人愿意围绕REST API构建丰富的生态系统。尝试从Open API规范生成React.JS客户端。与GraphQL客户端给你的体验相比,这种体验很糟糕。没有人想出一个好的商业模式来解决这个问题。进入GraphQL,你会得到大量的工具来抽象出你不想处理的问题。

REST API将变得与所描述的用例不那么相关,而不是因为GraphQL更优越。正是这些工具和生态系统将使GraphQL继续获得市场份额。与REST相比,GraphQL生态系统的扩张速度是巨大的。

超媒体API在服务器渲染的网络环境中扮演着重要的角色,现在仍在扮演着重要的角色。

.