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

发布于 2019-10-01  约 9 分钟
文章转发自专业的Laravel开发者社区,原始链接:https://learnku.com/laravel/t...

这个话题在开发社区里已经讨论过一段时间,人们对此有不同的看法与观点,那么我应该使用哪一个? 有很多东西需要成长但富有活力的新成员还是经验丰富的老成员? 在此之前让我们了解下 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 必然更好。因此在说哪一个更好之前, 需要分析一下所涉及的需求和数据, 才知道哪个更适合。

阅读 679发布于 2019-10-01

推荐阅读
PHP / Laravel / 全栈
用户专栏

PHP 和 Laravel 的精华文章分享。

4244 人关注
169 篇文章
专栏主页
目录