目录#
简介#
本文翻译自 REST vs GraphQL APIs, the Good, the Bad, the Ugly
自从Facebook发布GraphQL以来,在API世界中引起了一股 “用GraphQL替换REST” 的潮流。GraphQL确实解决了RESTful结构在开发中出现的诸多问题,但与之相对的,也带来了一揽子需要我们提前评估的风险。
GraphQL不只是 RESTful API 的简单的发展式迭代式的替换,所以本篇文章将详细阐述GraphQl的优缺点,以及什么样的项目适合使用GraphQL。
API的发展历史#
在RESTful API以前,有RPC、SOAP、CORBA以及其他各种小众的协议,这些古老的协议大多需要复杂的库支持来进行传递信息的序列化和反序列化。例如,与如今的RESTful API相比,SOAP其根本的差异在于它使用了一种叫做 WSDL(Web Services Description Language 网络服务描述语言)的强约定式的声明来进行类型约束,但这在从32位客户端接受64位上游节点传来的信息时,可能导致信息完全被破坏,这无疑是一场灾难。同时,SOAP不是一种技术架构,而是一个包含安全性、错误处理、ACID事物等等内容的协议的完整实现。而过多的抽象层导致了SOAP的复杂性,比如它能在HTTP层、TCP层、UDP层等等协议层上运行。但这种过多的抽象(在协议实现的层面上),也意味着一个应用的客户端和服务端,在数据层面产生了强耦合。
RESTful是在2000年左右由Roy Fielding在一篇博士论文中提出的,它是一种更为简洁的、只在http协议层使用的、无状态无类型的技术架构,这使得系统之间的耦合性大大降低,容错率大大提升。
如今的RESTful#
在开发API以及搭建应用中,REST API已经成为了约定俗成上的标准。REST的优雅之处在于前端开发者并不需要额外安装库或是初始化配置,请求通过简单的cURL或是浏览即可完成。
REST使用了标准的CURD Http语句(GET, POST, PUT, DELETE),并通过http约定来做到以数据为中心,而不是与协议去对抗。因此通过API api.acmestore.com/items
去操作一个电商信息,同通过API api.examplebank.com/deposits
去操作一个银行信息并无太大差别。这两者都需要进行CURD来操作资源,也可能更倾向于去cache操作的结果(也就是说 api.acmestore.com/items
和 api.examplebank.com/deposits
都会被cache),第三方开发者接触一个新API只需要大致了解这个资源的数据模型,剩余的就都交给RESTful的http约定即可,这样就能摆脱SOAP等那些旧方式需要深入了解成千上万个操作的窘境。换句话来说,REST同SOAP比起来,与http约定的绑定更为紧密,但提供了更为宽松的数据约定。
REST的问题#
随着越来越多的API被投入到生产生产环境中,并扩展到极限级别,RESTful架构中的某些问题出现了。在这种情况下,你可以将GraphQL视为一种介于SOAP和REST之间方案,从两者中各取了一部分。
从后端实现考虑#
在RESTful API中,服务器的响应是返回这个资源格式化后的数据。
但是,当客户端请求的是一些特定的东西,比如返回一个用户朋友的朋友的名字(他们的工作都是工程师),该怎么办呢?
在REST中,你可能会这样写:
1 | GET api.example.com/users/123?include=friend.friend.name&friend.friend.ocupation=engineer |
而在GraphQL中,允许你像这样编写请求的内容描述:
1 | { |
获取多个资源#
获取的信息相比REST有更少的冗余是GraphQL的主要优点之一,我们可能都经历过需要首先通过 GET /user
来获取每个用户的基本信息,然后逐个去请求如 GET /user/:id/friend/:id
这样的节点来获取每个朋友的具体信息,这会导致N+1问题,换句话来说,RESTful API在获取到最终的结果前,需要进行一连串链式的请求,而GraphQL能让服务器在一个请求中即能返回你所需要的数据;
更深层的分析#
API分析对于GraphQL来说也是一个负面因素,因为目前相关的工具并不多。但支持GraphQL api的工具可以提供比RESTful api更多的信息。
GraphQL的问题#
缓存#
RESTful API可以利用HTTP规范中内置的缓存。与缓存相关的GET和POST语义定义很清晰,能较好的支持浏览器缓存、代理层和服务器框架,以下是一些相关准则:
- GET请求可被缓存
- GET请求存在于浏览器历史中
- GET请求可能书签化标记
- GET请求是幂等的
GraphQL不遵循HTTP规范进行缓存,而是使用单个端点。因此,由开发者负责确保非可变、可缓存的查询正确实现了缓存。
虽然你可以使用像Relay或Dataloader这样辅助理解GraphQL语义的工具,但仍然不能涵盖浏览器和移动缓存等内容。
减少无共享的架构#
RESTful API的美妙之处在于,它们很好地补充了无共享架构。举例来说,假定我们有一个api.moesif.com/v1/search
节点和一个api.moesif.com/v1/alerting
节点,对外来看,这两个节点看起来请求的是两个完全不同的REST资源,而对内来看,它们指向了两个在不同集群上的微服务节点。搜索服务是使用Scala构建的,警告服务是使用NodeJs编写的。通过Host或URL路由触发一个HTTP请求的复杂性比检查一个GraphQL查询和执行多个连接要低的多。
暴露任意请求#
尽管GraphQL的主要优点是使客户端能够只查询所需的数据,但是这也会导致问题:尤其是对于那些允许第三方客户端查询的开放式API,而这些查询行为是无法控制和预测,所以我们必须格外小心,以确保GraphQL查询不会导致昂贵的联接查询,从而降低服务器性能,甚至降低服务器的DDoS性能。而RESTful API可以限制只使用匹配使用的数据模型和索引。
查询的固定性#
GraphQL去掉了在API之上进行自定义查询DSL或副作用操作的功能。 例如,Elasticsearch API是RESTful,但也具有非常强大的Elasticsearch DSL,可以执行高级聚合和指标计算。而在GraphQL语言中,这样的聚合查询可能更难建模。
分析工具的限制#
RESTful api的优点在于,它在资源方面与网站一样遵循HTTP规范。这使得许多工具能够探测URL,比如api.moesif.com/health
,如果不确定,它将返回5xx。对于GraphQL api,你可能无法利用这些工具,除非您支持将查询作为URL参数,因为大多数ping工具不支持HTTP和请求主体。
除了ping服务外,很少有支持API分析或对API调用进行更深入分析的SaaS或开源工具。客户端错误在GraphQL API中显示为200 OK。现有的工具会出现400错误,因此无法使用,因此您可能会错过捕获API上发生的错误。也就是说赋予客户端更多的灵活性的同时需要更多的工具来捕获和理解API问题。
总结#
GraphQL API可能是令人兴奋的新技术,但是在做出使用这种体系结构的决策之前,权衡利弊是很重要的。 某些API(例如,那些实体很少且跨实体的关系的API)(例如分析API)可能不适合GraphQL。 而具有许多不同领域对象(例如电子商务)的应用程序则更适合使用GraphQL。
实际上,GraphQL与REST的比较就像比较SQL技术与noSQL。 在某些应用程序中,有必要在SQL DB 中对复杂实体建模。 而其他仅具有“消息”功能的应用程序(如唯一的实体是“事件”的大容量聊天应用程序或分析API)可能更适合使用Cassandra之类的东西。
完。