为什么我们不使用GraphQL? - Wundergraph


我认为GraphQL将改变世界。将来,您可以使用GraphQL查询世界上的任何系统。我正在建立这个未来。那么,为什么我反对使用GraphQL?
我个人的烦恼是,当前社区采取GraphQL的理由实际上与GraphQL无关。如果我们想推动采用,我们应该诚实并脱掉玫瑰色的眼镜。这篇文章是对Kyle Schrade的“为什么使用GraphQL”的回应(https://www.apollographql.com/blog/why-use-graphql/),我并不是要直接批评该文,我认为Kyle的文章应命名为“为什么使用Apollo”。
 
REST缺点
作者指出REST API具有一些缺点,以及GraphQL如何解决所有这些缺点:过度获取多个资源的多个请求,会产生基于嵌套数据上的瀑布网络请求,要获得这些嵌套数据,每个客户端都需要访问提供这些嵌套数据的每个服务。
解决过度请求可以通过编写另一个REST API作为特定用户界面的外观来解决。以Next.JS为例。接下来,您可以使用非常轻量级的语法来定义API。您可以将这些调用包装到API中并使它们在服务器端,而不是从客户端发出多个请求。
使用这种方法也可以解决过度获取和不足获取,因为您可以在将数据发送回客户端之前对其进行操作。所描述的模式称为“后端的前端”(BFF)。它不限于Next.JS之类的完整堆栈框架。您也可以为移动应用程序构建BFF。
使用BFF模式,客户端本身不必知道每个服务的位置。但是,实施BFF的开发人员需要了解服务格局。希望您对所有服务都有开放的API规范,并在开发人员门户中很好地介绍了这些规范。如果做到这样地步,编写BFF应该很容易。
使用GraphQL,仍然需要一个实现解析器的开发人员。实现解析器与构建BFF差不多是相同的任务,其逻辑非常相似。那么,真正的区别是什么?
BFF易于实现,因为有更多可用的工具。例如,如果将Next.JS之类的框架与swr挂钩(重新验证时失效)结合使用,则可以自动使用Etags进行缓存,并且可以使缓存失效。这减少了服务器和客户端之间发送的数据量。与GraphQL相比,它的数据更少,因为您没有发送查询有效载荷,并且如果响应仍然有效,服务器将以304(未修改)进行响应。此外,您不必使用像Apollo这样的重量级客户端。Vercel的库swr很小,非常易于使用。它带有对分页,挂钩的支持,并有助于非常有效地来回导航。
GraphQL保留了查询,但是要实现这一点会带来额外的开销。如果您没有使用像Relay这样的客户端(默认情况下会保留查询),则必须自己完成操作或使用第三方库来实现它。与使用Next.JS的BFF方法相比,在前端获得相同结果要涉及更多的复杂性。您将如何使用GraphQL实现Etags?如果未做任何更改,如何使GraphQL服务器返回304状态代码?您不是首先必须将所有查询都转换为GET请求吗?如果是这样,您的GraphQL客户端和服务器是否轻松支持此功能?
在用户体验和易于开发方面,BFF无疑是赢家。客户端和服务器之间的数据传输更少。易于实施。客户更小,活动部件更少。
但是有一个陷阱。您必须为每个前端构建一个BFF。如果您有很多,那么可能会需要很多工作。您必须维护所有BFF。您必须对其进行操作。您必须保护它们。
如果您在不进行折衷的情况下同时获得两者的好处,那岂不是很好吗?这正是WunderGraph的含义,它是使用GraphQL构建BFF的框架。
 
没有更多的版本的API
在下一段中,Kyle继续探讨版本API所涉及的问题。他绝对正确,因为API版本过多会使跟踪变得非常困难。然后他得出结论,在GraphQL中,只有一个版本的图形,并且可以在架构注册表中跟踪更改,这是Apollo的付费功能。因此,您不会在版本控制方面遇到任何问题,他说。
我很难得出相同的结论。仅仅因为GraphQL模式本身不支持版本控制,并不意味着问题就消失了。如果您不对REST API进行版本控制,则将获得相同的效果。实际上,许多专家说,如果不需要也应该始终不要尝试引入API版本。话虽这么说,是什么让您无法运行两个版本的GraphQL模式?我并不是说这是个好主意,但在技术上是可行的。
如果您的组织中存在太多的REST API版本问题,那么在抛出类似GraphQL之类的新工具之前,也许您应该首先看一下组织。有这么多版本的原因是什么?也许流程的改变或新的团队结构会有所帮助?GraphQL绝对不会解决您的版本问题。相反,我认为这实际上使情况变得更糟。
您必须支持移动应用程序吗?您应该意识到,交付本地应用程序需要时间。您必须等待应用商店的批准,并且可以期望许多用户从不(或缓慢地)安装新版本。如果您想在不破坏客户的情况下在这种情况下进行重大更改怎么办?不可能。您必须以不间断的方式引入此类更改。
在GraphQL的情况下发展模式将意味着您不赞成使用旧字段并添加新字段。新客户使用新字段,而您希望使用旧字段的客户数量会越来越少。希望您拥有一个可以迫使您的用户在某个时间点下载新版本的系统。否则,您可能会被迫无限期地支持不推荐使用的字段。如果是这种情况,GraphQL的弃用模型根本无法帮助您。
使用REST,您可以创建一个新的端点或现有端点的另一个版本。问题是一样的,解决方案看起来有点不同。
明确地说,如果您无法控制客户那边的开发,则就确实需要某种版本控制。如果您只有一个Web应用程序,则不需要此功能。但话又说回来,GraphQL可能也太过杀手了。
  
更小的载荷
在本段中,原文作者Kyle声称RESTful API不允许部分响应。
这是错误的。这是一个例子:

GET /users?fields=results(gender,name)

作者Kyle实际上是什么意思?我很确定他知道部分回应。我想他想说的是某人需要实施部分响应。实际上,当您从资源中选择子字段时,它对于GraphQL看起来非常熟悉。使用GraphQL,我们可以立即使用此功能。
另一方面,使用BFF方法,则不需要此方法。只需准确返回所需的数据即可。同样,像Next.JS这样的全栈框架使实现此过程更简单,使缓存更加容易,并且使您免费获得基于Etag的缓存失效。
总结本节,GraphQL可以为您提供所需的确切数据。部分响应可以达到相同的结果。BFF附带了实施和维护的额外费用,但具有更好的UX和DX。
 
严格类型接口
在本段中,Kyle解决了未严格键入REST API的问题。他谈到了API的问题,这些问题尚不清楚,如果您获得大量帖子或其他内容的数组,那么查询参数如何使情况复杂化。他还指出,由于GraphQL具有严格的类型系统,因此不会出现此问题。
我认为Kyle所说的是一个组织问题,您需要一个组织解决方案。
当您允许开发人员部署REST API而不发布Open API Specification(OAS)或类似内容时,您会遇到他所描述的那种问题。使用OAS,可以很容易地描述所有资源。OAS还允许您描述每个端点的OAuth2流和所需范围。此外,您可以描述查询参数的确切类型和验证规则,这是GraphQL所缺少的功能。
从GraphQL来看,无法描述身份验证,授权和输入验证。GraphQL缺少这些功能,因为Facebook的发明者在不同的层上解决了这个问题。无需他们将这些功能添加到GraphQL。您可以向架构中添加自定义指令,以实现类似于OAS的结果,但这将是您必须维护的自定义实现。
您可能会认为OAS无法保证API的响应符合规范。你说的没错。但是GraphQL模式如何保证任何内容?
GraphQL自省是将特定的GraphQL查询发送到服务器以获取有关GraphQL模式的信息的操作。GraphQL服务器可以自由选择所需的任何类型。如果您发送查询,则服务器可以使用内省响应中不符合GraphQL模式的响应进行回答。
以Apollo联盟为例。您将架构上载到架构注册表中,然后错误地部署了错误版本的GraphQL服务器。如果更改字段的类型,客户端可能会感到困惑。
当我们谈论GraphQL中的类型安全性时,实际上是指我们相信GraphQL服务器的行为完全符合自省查询响应所通告的行为。为什么我们不能以相同的方式信任Open API规范?我想我们可以。如果我们不这样做,那就是人员问题,而不是技术问题。
 
更好的客户端性能
下一段简短介绍了GraphQL如何提高客户端性能并减少网络往返次数。
我认为,与重量级的GraphQL客户端相比,我已经充分解释了BFF的功能强大得多,以及从“过时的重新验证模式”中获得了多少收益。
也就是说,GraphQL确实确实减少了请求数量并减少了整体数据传输。但是,您应始终考虑将GraphQL客户端添加到前端的成本。
 
减少记录和浏览API的时间

下一部分将介绍如何将OAS之类的工具用于RESTful API开发以及在微服务环境中维护多个OAS的挑战。Kyle将单个GraphQL模式与分布在多个git存储库中的Swagger文件进行了比较。
我认为很明显,导航单个GraphQL模式比坐在git存储库中查看多个OAS文件要简单得多。但是,公平地说,我们必须将在一个苹果与另外一个苹果进行比较。如果您想提高开发人员的工作效率,就不会将OAS文件粘贴到git存储库中,而是每天都将其命名。您运行的是一个开发人员门户,您可以在其中搜索API并在它们之间导航。
OAS依赖于JSON-Schema,该功能具有惊人的功能:您可以引用另一个文档中的对象类型。您可以将OAS分为多个文件,如果需要,它们可以互相引用。还有工具可以将多个OAS文件合并到一个OAS文档中。然后,您可以使用此文档,并将其输入到开发人员门户中,该门户可让您整体浏览所有API。请记住,设置所有这些都会产生额外的费用。您需要运行一个开发门户或购买一个。您必须描述所有的API,至少在开始时,这可能是一种负担。
要添加的一件事是,有许多框架可以让您用自己喜欢的编程语言来描述架构,例如通过定义对象或类。然后,您将获得一个自动生成的Open API规范,该规范将在知名端点上提供。
让我们将其与GraphQL进行比较。基本上有两种方法,首先是SDL,然后是代码。无论采用哪种方式,最终都会得到一个GraphQL模式,该模式描述所有类型以及字段,并允许您对其进行注释。
那么,有什么区别呢?OAS带有更多的开销来进行设置。另一方面,OAS具有内置的支持文档记录示例用例,身份验证和授权以及输入验证规则。
请记住,GraphiQL本身没有多个GraphQL模式的概念。如果您具有多个GraphQL(微)服务,则必须运行或购买专用组件,例如,类似于REST API开发人员门户的架构注册表。
我想专门介绍一个额外的段落是API用例。仅仅因为您拥有OAS或GraphQL模式,并不意味着您的API有充分的文档记录。API用户可以使用该API做什么?他们如何使用它?什么是好用例?有什么不好的?在哪里寻求帮助?如何验证用户?我需要API密钥吗?用一种可以帮助API使用者使用API​​的方式来记录您的API,比在类型和字段中添加描述要多得多。OAS允许您添加示例有效负载并对其进行描述。GraphQL缺少此功能。在Stripe上看到一个积极的例子。它们远远超出了Swagger或GraphQL Playground所能达到的范围。
另一方面,如果您查看GitHub的公共GraphQL API,您将找不到单个示例查询。假设您要获取有关存储库及其所有问题的信息。您必须打开GraphiQL并开始搜索。但是,GraphiQL中的搜索功能并没有真正帮助您。有人需要坐下来写示例查询和有关如何使用API​​的用例。否则,真的很难上手。
因此,尽管社区一直在说“ GraphQL正在自我记录”,但仅此功能并不能提供有用的API。OAS为您提供了添加用例的工具,但是您仍然必须编写它们。无论您选择哪种工具或语言,都需要努力使API对其他人有用。
 
传统应用程序的支持
在这一段中,作者说,为移动应用保留旧版本的REST API很痛苦。他的结论是,由于我们仅使用单个GraphQL服务器,因此不会出现此问题。
抱歉,我再次得出完全不同的结论。如果将规则设置为禁止版本控制,则可以添加新端点或交换现有端点的实现。在这种情况下,GraphQL和REST之间没有区别。对于REST和GraphQL API而言,支持旧版应用程序都是一个挑战。您需要找到一种不破坏客户端和服务器之间合同的方法。服务器是否公开REST或GraphQL都无关紧要,问题是相同的。
  
更好的错误处理
关于错误处理,作者描述了一个场景,即与将用部分数据进行响应的单个GraphQL查询相比,客户端必须进行3个后续REST API调用。
使用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是API的未来,但出于不同的原因。
更好的性能和更小的有效负载大小不是GraphQL的独特功能。您可以使用其他工具获得更好的结果,或者必须扩展GraphQL,例如使用Relay来获取持久化查询。为了从GraphQL文档中获得真正的好处,您肯定要做的不只是在架构中添加描述。
一旦尘埃落定,炒作就消失了,我们必须看看事实。我们不应该试图让世界相信GraphQL。使用GraphQL有很多优点,但是根据您的用例,您可能不会真正从中受益。与其说GraphQL是圣杯,不如说是更微妙的回应。
解决此问题的最佳方法是先查看问题,然后对解决问题的可能工具进行独特的比较。如果您的组织无法实现REST API,那么GraphQL又能如何解决此问题?也许您组织内的某些事情必须改变?另一方面,如果这不是组织上的问题,并且您完全确定REST不是您的用例的好选择,那么我敢打赌,您一定会喜欢GraphQL。
 
GraphQL本身几乎没有用。而是GraphQL工具使GraphQL如此强大。是社区,是我们!像Apollo,Hasura,The Guild,FaunaDB,Dgraph,GraphCMS之类的公司,我也希望WunderGraph也如此,它们使GraphQL如此强大。它是GraphiQL,各种GraphQL客户端,模式缝合,联合。整个工具生态系统是GraphQL成为下一件大事的原因。
更多工具和服务将加强生态系统。更强大的生态系统将导致更多的采用,这又将吸引更多的公司为GraphQL生态系统添加服务和工具,这是一个非常积极的循环。
GraphQL在这方面与Kubernetes非常相似。Docker(容器运行时)还不够。将复杂的Syscall封装为易于使用的API是使能因素,但是为了创建一个丰富的生态系统,需要一个调度程序,该调度程序应具有足够的表现力并具有非常容易的扩展性。
GraphQL提供了一种与Docker一样简单的语言来定义和使用API​​。如果您以前看过几行Javascript和JSON,则GraphQL会立即让您感到熟悉。但是类似于Docker,该语言本身并不强大。它是可扩展性及其周围的工具。
 
当Kyle问“ Why GraphQL”时,我认为他的实际意思是“ Why Apollo”。答案很简单。没有人愿意围绕REST API构建一个丰富的生态系统。尝试根据开放API规范生成React.JS客户端。与GraphQL客户给您的东西相比,这种体验很烂。没有人想出一种好的商业模式来解决这个问题。输入GraphQL,您将获得大量工具来抽象出您不想处理的问题。
REST API与所描述的用例的相关性将降低,而不是因为GraphQL是高级的。它是使GraphQL继续获得市场份额的工具和生态系统。与REST相比,GraphQL生态系统的扩展速度非常快。
超媒体API在服务器呈现的Web应用程序中一直发挥着巨大作用,并且仍然扮演着重要角色。但是,网络正在向前发展。用户期望从网站获得的原生体验。Jamstack正在接管前端。具有服务器端渲染和动态Javascript客户端的混合模型是这些应用程序的实现者。
RESTful API擅长解决一系列不同的问题。他们不会消失,恰恰相反!对于行业正在转变的这种类型的应用程序,它们并不是正确的工具。我认为REST API是内部API,合作伙伴API和服务器到服务器通信的理想工具。这是GraphQL在REST方面并未真正带来任何好处的领域。与RPC一起,它将在这一领域拥有美好的未来。