API 接口设计: GraphQL 和 REST 怎么选择?

这个话题在开发社区里已经讨论过一段时间,人们对此有不同的看法与观点,那么我应该使用哪一个? 有很多东西需要成长但富有活力的新成员还是经验丰富的老成员? 在此之前让我们了解下 REST 和 GraphQL吧。

REST 是什么?

REST即表述性状态传递(英文:Representational State Transfer,简称REST),它符合特定的指南,是 Web API 实现的约束。是 Roy Fielding 博士在他的博士论文中提出来的一种软件架构风格。它鼓励客户端和服务器以无状态模式交换信息。 请记住,并非所有 API 都是 REST,但所有 RESTful 服务都是 API。

GraphQL 是什么?

GraphQL是Web API 的查询语言。它由Facebook于2012年创建,并于2015年开源。它既不是架构模式,也不是Web服务。它是个中介,用来查询从各种数据源接收的数据。 这些数据源可以是数据库或Web服务。

多年来,REST已成为Web API的事实上的标准。 由于它使用了标准的HTTP方法(GET,POST,PUT,DELETE等),随着互联网上的Web应用程序的增加,它也得以发展和普及。 此外,它的语言和平台无关,使其成为创建Web服务的更好选择。 因为每个数据都被视为在调用URL时要发送的资源,所以甚至可以使用Web浏览器或使用cURL请求来调用它。

REST的缺点

虽然REST非常成功,但由于RESTful服务的规模和复杂性不断增长,因此它的缺点变得非常明显

1. 多端 (多次数据交互)

在 RESTful 服务中一个 URL 表示一个资源。因此,当要获取多个资源时你必须请求多个不同的 URL,进而带来多次数据交互。

当我们考虑一个博客应用。一篇博客下面有多条评论的情形。通常我们要调用的 URL 如下

GET /posts/<postId> - 获取特定的博客文章
GET /posts/<postId>/comments - 获取上面博客文章关联的所有评论
GET /posts/<postId>/comments/<commentId> - 获取特定博客下的特定评论

你会发现我们要请求的 URL 多了不少。这是因为实体 (这里可以理解为博文和评论)之间的关联关系更加复杂了。随着应用变得越来越复杂,管理这些 API 也变得更加困难。

2. 过度获取/获取后 数据

有时候,当您请求 API 接口时,您会获得不必要的数据和相关数据,有时候您无法获得足够的数据,所以您最终会进行多次往返。 这是 RESTful 服务中的常见问题。 在某些情况下,您可能只需要 2 - 3 个值,但您可以获得大约 20 - 25 个值作为响应。 这只会通过增加响应时间,导致传输大量未使用的数据。 在后一种情况下,您获取的信息可能需要比从单个 URL 获取的信息要多,因此有必要进行多次往返。 这也导致客户端获取所有所需数据所花费的时间成本增加。

3. API版本控制

API 版本控制是一种遵循的方法,以避免使用响应格式的更改来破坏客户端应用程序。 当 API 响应格式发生更改时,将创建新版本。 这样做是为了使生产客户端应用程序可以按预期运行,并为开发人员提供一些休息的时间来迁移到新的 API 版本。

但是这个版本控制是一个问题,因为当新版本发布时,它意味着新的 URL。 API 的维护和使用变得困难,并且经常导致重复的代码。

4. 弱类型

并非我们从 RESTful 服务收到的所有数据都是强类型的,即它们没有正确地给出特定数据。 这在记录 API 时会成为问题,因为我们必须通过调用 URL 来指定客户端可以期望的数据类型。

5. 客户端被蒙在鼓里

在收到响应结构之前,客户端不知道响应结构。所以,客户端是被蒙在鼓里的。这可能经常导致一些错误和数据无法正确处理,从而降低了消耗 API 的可靠性。

GraphQL 的优点

GraphQL 是由 Facebook 发明的,主要是为了克服 REST 的缺点。

1. 一次请求获取到所有

一个 GraphQL 服务只暴露一个端点以便客户端能传输必要的查询去检索数据。 使用前面考虑过的相同示例,让我们看看 GraphQL 查询

{
    findPost(id: <postId>) {
        id
        title
        content
        author
        comments {
            id
            comment
            commentedBy
        }
    }
}

正如你所看到的,我们仅仅通过单个请求获取到了所有必要的数据。 所以当你想要一个新字段你只需要将它添加进查询中,它将在响应中呈现

2. 强类型

GraphQL 被强类型模式所控制。这些类型既可以是原始的也可以是派生的。强类型系统允许 API 自文档化,从而使客户端知道在查询特定查询时会的到什么响应。

3. 客户端驱动

GraphQL 提供了一种声明式语法,以便客户端精确地指定它们所需的字段。 这消除了由于客户端根据模式向 GraphQL 服务器声明其数据需求而导致数据冗余和不充分的可能性。

4. API 演变

因为在 GraphQL 中一切都是模式(schema)驱动, 新增字段不会影响现存字段,而且 GraphQL 还为废弃字段提供 @deprecated 注释,所以对 GraphQL 的扩展并不是问题。 这就消除了 API 版本控制的需要。

5. 传输层不可知

这是 GraphQL 的一个非常棒的优点。 API 服务器可以通过类似 HTTP, HTTPS, WebSockets, TCP, UDP 等协议进行信息交换。 这是因为 GraphQL 甚少关心信息如何在客户端与服务器之间进行交换。

GraphQL 的缺点

哇,GraphQL 很棒,这是一个众所周知的事实。但是世界上的任何东西都是有缺陷的,GraphQL 也无法置身事外。

1. 缓存功能不成熟

GraphQL 不支持浏览器和移动手机缓存,这一点区别于使用本地 HTTP 缓存机制的 RESTful 服务,因此我们要为实现 GraphQL 缓存付出额外努力。虽然有 Relay 这样的工具提供了一些缓存支持,但是它们还没有 RESTful 服务使用的缓存机制成熟。

2. 检验与错误报告

RESTful 服务利用 HTTP 状态代码来处理可能遇到的不同错误。对开发人员来说,这使得 APIs 的检验变得非常简单和轻松。但是使用 GraphQL 服务总是返回 200 OK 响应。一个典型的 GraphQL 错误消息是这样的,状态码为 200 OK

{
    errors: [
        { 
            message: 'Some error occurred'
        }
    ]
}

这使得处理错误场景非常困难,并且使得检验过程非常麻烦。

3. 暴露模式和资源攻击

和RESTful服务不同,GraphQL服务要求客户端必须知道要查询的数据模式。 如果您将API暴露给第三方,则基本上暴露了您的内部数据结构。 必须非常小心,因为客户端不用很高的代价就可以发起连接查询,这可能会导致服务器上的拒绝服务(DoS)攻击。

4. 安全 - 身份验证和授权

GraphQL社区仍然对如何处理GraphQL服务的安全部分感到困惑。仍然没有集成身份验证和授权的原生解决方案。它通常被抽象到业务逻辑层来授权用户,但是我们是否真的必须解析和验证一个未经验证的用户的查询仍然是GraphQL领域中的一个问题。

5. N + 1 次查询问题

在RESTful服务中,很容易记录执行的SQL查询并进一步优化它。但在GraphQL的情况下,解析性质是动态的,因此很难获得精确的查询并进一步优化它。有时,字段解析器可能会导致N+1次查询问题和复杂的连接操作。但是Facebook正在开发像DataLoader这样的工具来解决这个确切的问题。所以,也许在未来,这不会是一个不利因素。

6. 年轻的生态

GraphQL在这个API生态系统中非常像一个婴儿,这意味着随时可能会出点问题以及破坏性更改,因此在使用与GraphQL相关的任何库和模块时,我们必须非常细心。

总结

首先我要说GraphQL只是一种工具,REST是一种架构模式。如果说用GraphQL取代REST,那就大错特错了。但是在这个微服务的时代,我们将API分离并创建到原子级别,我们可以利用这两个方面的优势,因为并没有银弹。

GraphQL服务将性能作为首要任务,而RESTful服务则保持可靠性。

GraphQL节点可以通过现有的RESTful服务作为节点公开,比如/ graphql,它可以作为运行GraphQL查询的网关,同时也可以为某些场景维持REST节点。

在某些场景中, 使用 GraphQL 会更好, 也有一些场景中 REST 必然更好。因此在说哪一个更好之前, 需要分析一下所涉及的需求和数据, 才知道哪个更适合。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://dev.to/sadarshannaiynar/graphql-...

译文地址:https://learnku.com/laravel/t/28508

本帖已被设为精华帖!
本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 11
Epona

各打五十大板!

4年前 评论
codinget

GraghQL 构建的时候也并不容易,而且还要牺牲开发效率、运行效率、安全性、小公司以及项目管控能力差的团队现阶段最好别考虑这个东西。

4年前 评论

翻译文章中的端,原文中是 endpoint,感觉可能不太好理解,大家可以理解为 URL 就好了。

英文好的可以直接看这里关于 endpoint 和 resource 之间的差别。
https://stackoverflow.com/questions/305805...

4年前 评论

@codinget 我们有两个项目使用了 GraphQL ,是有一定的学习成本。但是一旦学会,效率嗖嗖的。 使用 laravel 的话,推荐 lighthouse ,非常方便。API 文档也不用写了。

4年前 评论

@zhuzhichao 大佬有机会可以分享下。找到一篇相对接地气的 GraphQL 聚合层的实际案例分享,有需要的同学可以看下 GraphQL 解决了哪些问题(API 受制于页面、Mock 职责重合、前端报表输出慢)以及实际的收益

传送门>>

4年前 评论

@zhuzhichao 是否适合用在业务比较稳定的 API, 版本迭代更替不会太快的这种?

4年前 评论

@skyLee 适不适合做公司项目,主要看包是否功能完善,是否发布稳定版本,是否还在维护,项目是否活跃,我个人觉得 lighthouse 是可以的。一般优秀的开源项目,迭代都很快都。 :sweat_smile:

4年前 评论

@zhuzhichao 看了你的推荐,了解了下,不错,非常好!

4年前 评论

GraphQL 灵活高效 任何查询都能满足 (不开心的是 没有找到数组格式的Type) 已经在项目中使用

4年前 评论

@大约在冬季 数组格式就是用 [] 把你的内容包裹起来。

4年前 评论

@zhuzhichao 你说的那个我知道 我说的是在 Type:: 中没有找到数组类型的 暂时用的Type::string() 然后resolve 转成的json :joy:

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!