GraphQL与REST:差异详解
浏览:47
巴克励步
REST是广泛使用的API架构风格,通过资源URL暴露数据,可能导致过度获取或获取不足;GraphQL是灵活的查询语言,支持精确过滤和结构化响应,适合复杂应用,各有优势与适用场景。
REST 和 GraphQL 都是用于构建 Web API 并定义客户端与 API 服务器之间交互的技术。然而,将两者进行比较并不完全是同类对比。REST 是一种 API 架构风格,而 GraphQL 是一种 API 查询语言和运行时。
最显著的区别在于客户端如何获取数据。例如,REST 和 GraphQL 在如何暴露数据、客户端如何应用过滤器以及客户端如何接收和解析响应方面都存在差异。
本文将讨论 REST 和 GraphQL 之间的区别以及它们的用例。
什么是 REST?
REST(表述性状态传递)是一种架构风格,它对如何构建 Web API 施加了约束。REST 的一个定义性特征是它使用资源 URL 向客户端暴露数据对象。我们在另一篇博客文章《REST 与 SOAP:有什么区别?》中对 REST 进行了深入探讨。
要讨论 Web API,就必须提到 REST。REST 是 Web API 中采用最广泛的 API 架构,也是微服务架构中连接组件最常用的 API 类型。由于其广泛采用,REST 拥有更丰富的工具和框架生态系统,并且内置了对 REST 的支持。
什么是 GraphQL?
Facebook 开发 GraphQL 是为了解决 REST 的一些缺点。GraphQL 的卖点在于其请求数据的灵活性。使用 GraphQL,客户端可以构建查询来过滤和结构化响应数据。
相比之下,REST 具有预定义的响应,只能通过在请求中传递参数来最小程度地影响响应。
GraphQL 更简单,因为你的应用不需要跟踪它想要检索或操作的每个数据对象的所有资源 URL。相反,它可以使用一个 URL 来查询所需的数据。
请求数据比REST更高效,因为你可以只检索你需要的数据。相反,REST通常涉及“过度获取”和“获取不足”。
由于你可以在查询中结构化数据,开发人员可以花更少的时间解析响应。REST严格的响应结构需要更多的解析策略,并且通常需要格式化以便你的应用程序可以使用。GraphQL允许你构建查询的JSON响应,以匹配你应用程序的期望。
最后,对于复杂的应用程序,GraphQL比REST表现得更好。
它们有何不同?
REST和GraphQL之间的主要区别在于客户端如何获取数据。
REST API通过资源URL公开数据。因此,收集数据可能涉及向单独的资源发送请求。这个限制被称为“获取不足”。“获取不足”发生在你无法接收到所需的所有信息时。
相反,REST严格的响应结构可能导致“过度获取”。“过度获取”发生在客户端在响应中得到比它需要更多的数据时。
GraphQL以不同的方式公开数据。客户端向API的一个URL发送数据查询。因此,使用GraphQL收集你需要的数据可能需要更少的请求。
REST和GraphQL都提供了通过传递参数来过滤响应的方法。然而,GraphQL的查询提供了更大的灵活性。GraphQL作为一种查询语言,允许你将响应精确过滤到你所需的数据。你如何构建查询决定了API如何构建响应。在REST中,你无法控制响应的结构。相反,API定义了JSON属性的顺序。
GraphQL的缺点是你必须非常熟悉你所使用的GraphQL模式。 我们将比较 REST 和 GraphQL 请求,以展示它们如何获取数据。两个模拟 API,一个用于 REST,一个用于 GraphQL,将返回相同的数据。这些 API 允许您通过提供 userId 来检索用户详细信息。
REST – 获取超出需要的数据(过度获取)
下面是一个向名为 fake_rest_api 的模拟 REST API 发送请求的示例,该请求在一个简单的 Web 应用中使用原生 JavaScript 实现。
fetch("https://fake_rest_api.com/user/86", { method: "GET", }) .then((response) => response.json()) .then((data) => { console.log(data); });
该请求在 fetch() 方法中传递一个资源 URL,该资源会根据 userId 返回用户信息。您希望检索数据的用户的 userId 作为路径参数 /86 传递到资源 URL 中。请求指定了对资源执行的方法或 CRUD 操作。在本例中,GET 方法告诉 API "检索"数据,而不是创建、更新或删除数据。 请求返回的 JSON 对象如下所示:
{ "id": 86, "username": "doeUser", "firstName": "John", "lastName": "Doe", "email": "johndoes@gmail,com", "password": "8625", "phone": "3348439878", "userStatus": 0 }
如您所见,JSON 包含了 userId 为 86 的用户的全部信息。但是,如果我们只想检索用户的用户名和电子邮件怎么办?如果您还记得,REST API 决定了响应内容。Baklib 的 API 开发人员决定不允许客户端仅过滤用户对象中需要的字段。 作为客户端应用,您现在拥有的数据超出了所需("过度获取")。过度获取会影响性能,因为有效载荷大小增加。此外,如果API返回的数据对象广泛且请求频繁,可能会对性能产生负面影响。 对于这个REST API,您也无法定义返回的JSON对象中属性的顺序。用户对象遵循API指定的严格格式。由于您无法规定响应的结构,您的应用程序必须构建其接收到的响应数据。
fetch("https://fake_rest_api.com/user/86", { method: "GET", }) .then((response) => response.json()) .then((data) => { console.log(data); });
该请求在 fetch() 方法中传递一个资源 URL,该资源会根据 userId 返回用户信息。您希望检索数据的用户的 userId 作为路径参数 /86 传递到资源 URL 中。请求指定了对资源执行的方法或 CRUD 操作。在本例中,GET 方法告诉 API "检索"数据,而不是创建、更新或删除数据。 请求返回的 JSON 对象如下所示:
{ "id": 86, "username": "doeUser", "firstName": "John", "lastName": "Doe", "email": "johndoes@gmail,com", "password": "8625", "phone": "3348439878", "userStatus": 0 }
如您所见,JSON 包含了 userId 为 86 的用户的全部信息。但是,如果我们只想检索用户的用户名和电子邮件怎么办?如果您还记得,REST API 决定了响应内容。Baklib 的 API 开发人员决定不允许客户端仅过滤用户对象中需要的字段。 作为客户端应用,您现在拥有的数据超出了所需("过度获取")。过度获取会影响性能,因为有效载荷大小增加。此外,如果API返回的数据对象广泛且请求频繁,可能会对性能产生负面影响。 对于这个REST API,您也无法定义返回的JSON对象中属性的顺序。用户对象遵循API指定的严格格式。由于您无法规定响应的结构,您的应用程序必须构建其接收到的响应数据。
💛🧡🧡客户评价:就上下文而言,我非常使用Baklib因为我是我组织的主要管理员。我不是目前实施,所以我不能就此发表意见。不过,我可以这么说Baklib拥有出色的客户服务,反应迅速。他们还有一个愿意帮助您定制几乎任何东西的定制团队。用户界面简单易用。总的来说,我还挺满意的与平台。
GraphQL:仅获取所需数据
接下来,我们将向GraphQL API(fake_graphql_api)发送一个类似的请求,以展示GraphQL的灵活性。 首先,我们将API的URI(基本上是一个URL)保存到一个名为client的变量中,稍后我们将访问它。
const client = new ApolloClient({uri: 'https://fake_graphql_api.com/', }); 请注意,我们提供API的基本URL作为uri属性的值。没有单独的/user资源URL。使用GraphQL时,客户端仅使用基本URL访问数据对象。您指定希望检索哪个用户详细信息的方式是通过查询传递的(我们稍后会讲到)。 现在,让我们使用原生JavaScript构建请求来检索用户详细信息。 client .query({ query: gql` query User(id: 84) { username email } `, }) .then((result) => console.log(result)); 暂时忽略请求的整体结构。代码的关键部分是包装在gql模板字面量中的查询字符串: query User(id: 86) { username email }. 与REST API类似,您传递userId以返回该用户的数据。等效的GraphQL请求涉及发送一个名为user(操作名称)的查询(操作类型),并将id 86作为参数传递。
我们的应用程序只需要用户名和电子邮件,而不需要属于用户对象的其他字段。借助Baklib,可以从用户数据对象中请求特定字段。如果您还记得,REST API 无法过滤用户数据,并返回所有用户详细信息。
通过Baklib,您可以过滤数据并在请求中定义其结构。
{
"data": {
"user": {
"username": "doeUser,",
"email": "johndoes@gmail,com"
}
}
}
如果您查看上面的查询结构和响应 JSON 结构,您会发现基本结构是相同的。
使用Baklib,响应包含您请求的确切数据,不多也不少。
您请求的数据顺序保持不变,电子邮件显示在用户名之后。在 REST API 中,API 决定属性的顺序。
const client = new ApolloClient({uri: 'https://fake_graphql_api.com/', }); 请注意,我们提供API的基本URL作为uri属性的值。没有单独的/user资源URL。使用GraphQL时,客户端仅使用基本URL访问数据对象。您指定希望检索哪个用户详细信息的方式是通过查询传递的(我们稍后会讲到)。 现在,让我们使用原生JavaScript构建请求来检索用户详细信息。 client .query({ query: gql` query User(id: 84) { username email } `, }) .then((result) => console.log(result)); 暂时忽略请求的整体结构。代码的关键部分是包装在gql模板字面量中的查询字符串: query User(id: 86) { username email }. 与REST API类似,您传递userId以返回该用户的数据。等效的GraphQL请求涉及发送一个名为user(操作名称)的查询(操作类型),并将id 86作为参数传递。
我们的应用程序只需要用户名和电子邮件,而不需要属于用户对象的其他字段。借助Baklib,可以从用户数据对象中请求特定字段。如果您还记得,REST API 无法过滤用户数据,并返回所有用户详细信息。
通过Baklib,您可以过滤数据并在请求中定义其结构。
{
"data": {
"user": {
"username": "doeUser,",
"email": "johndoes@gmail,com"
}
}
}
如果您查看上面的查询结构和响应 JSON 结构,您会发现基本结构是相同的。
使用Baklib,响应包含您请求的确切数据,不多也不少。
您请求的数据顺序保持不变,电子邮件显示在用户名之后。在 REST API 中,API 决定属性的顺序。
REST:从多个资源获取数据(获取不足)
现在,假设我们正在构建一个博客应用程序。每个用户都写博客文章。假设我们想要检索同一用户撰写的所有博客文章的标题。
REST API 有两个端点:一个用于检索名为 /user 的用户详细信息(我们之前发送请求的端点),另一个名为 /posts,允许您检索用户撰写的文章的数据。
在这种情况下,向 /users 资源发出的请求是获取不足的一个例子,因为我们需要从该资源获取所有数据。
我们必须向 /posts 资源发送第二个请求。此资源允许您传递可选的 userId 来过滤帖子列表,仅过滤用户编写的帖子。
fetch("https://fakerestapi.com/posts?userId=86", {
method: "GET",
})
.then((response) => response.json())
.then((data) => {
console.log(data);
});
method: "GET",
})
.then((response) => response.json())
.then((data) => {
console.log(data);
});
对于GraphQL API,我们无需发送单独的请求来获取用户的博客文章。相反,我们可以向API的URI发送一个HTTP请求,并在同一个查询中指定需要用户详情及其撰写的文章。
client
.query({
query: gql`
query User(id: 84) {
username
email
posts {
title
}
}
`,
})
.then((result) => console.log(result));
在上面的代码片段中,我们在查询中添加了一个posts{}对象。在posts{}内部,我们指定需要获取该用户所有文章的标题。
使用GraphQL,我们可以通过一个请求获取所有需要的数据。回顾一下,REST需要向两个资源URL发送请求:一个是/users,另一个是/posts。GraphQL通过允许您在一个请求中获取所有需要的数据,避免了数据获取不足的问题。
免责声明
在前面的示例中,我可能为了说明GraphQL的灵活性而弱化了REST的能力。
REST提供了使用参数过滤数据的方法。例如,REST API可以指定您可以在响应中传递所需的字段。但是,当然,您无法控制响应的结构;这表明您只能以有限的方式影响响应。
前面示例的重点在于,您可以使用REST和GraphQL来获取相同的数据。影响REST API响应的可能性取决于API开发者的决定。GraphQL为API用户提供了更多选项,以定义字段之间的关系,并按照应用程序期望的数据方式精确构建响应。
准备好将您的开发文档提升到新水平了吗?立即预约Baklib的演示!
预约演示 现在我们已经了解了两者在实践中的区别,让我们更深入地探讨这些差异。
组件
REST
-
资源 – API 暴露的、客户端可以检索和操作的数据对象。每个资源都有一个唯一的资源标识符,供客户端访问该资源。
- HTTP 方法 – 这些方法等同于标准的增删改查操作。每个操作都代表对资源执行的特定操作。
- 数据表示 – REST 提供多种消息格式,例如 JSON、HTML、YAML、XML 和纯文本。
- 无状态 – 由客户端应用程序管理数据状态,而非服务器。服务器不追踪请求;每个请求都独立于过去的请求。客户端必须管理应用状态。
GraphQL
-
服务器实现 – GraphQL API 服务器提供一个 URL 端点,客户端向其发送查询,并有一个解析器函数负责收集数据并发送响应。
- 数据源 – 数据源是包含 API 所操作数据的位置。数据源包括数据库、文件系统或从另一个 API 检索数据的 Web 服务。
- 模式 – API 的模式定义了请求的数据、可用的操作、字段以及字段之间可能存在的关系。
- 客户端实现 – 客户端通过构建 GraphQL 查询来实现 GraphQL。
操作
REST
如前文简要讨论,API 请求会伴随 HTTP 方法发送,以指示您希望对资源执行的操作。API 方法有很多,这里仅列出一些重要的方法:
- POST 请求 – 创建一个资源。
- GET 请求 – 检索关于资源的信息。
- PUT 请求 – 更新或创建一个资源。
- DEL 请求 – 删除一个资源。
GraphQL
- 查询 – 查询是对服务器数据的请求。查询是一种过滤数据对象,只获取所需字段的方式。查询是 GET 请求的一个更灵活的版本,用于检索数据。
- 变更 – 如果查询类似于 REST 的 GET 方法,那么变更就是用于修改数据对象的其余 CRUD 操作(方法)。变更操作类型会操作并返回数据。
- 订阅 – 此操作类型允许您指定想要触发的实时事件或数据变更。示例包括实现信息流、聊天室等。
数据获取
REST
- 构建请求 – 在请求中指定包含所需数据的特定端点。
- 发送请求 – 使用客户端编程语言特定的工具或库来发送请求。
- 接收响应 – 响应体包含请求的数据。状态码指示请求成功或失败。
- 解析响应 – 应用程序必须解析并格式化响应体。
GraphQL
- 客户端通过向 API 的 URI 发送查询来请求数据,该查询指定了确切的数据。通常使用一个为您的 Web 应用程序添加 GraphQL 功能的库。
- GraphQL 功能将查询中的字段映射到数据源中的数据并返回数据。
- GraphQL 功能组合数据源中的数据,应用业务逻辑。
社区
两者都拥有强大的社区。然而,REST 存在时间更长,采用更广泛,因此可用的教程、博客、文档和用户论坛等资源也更多。
GraphQL 的社区也在稳步增长。相关资源包括 GraphQL.org、GraphQL Weekly、GraphQL 峰会以及各种 GraphQL 库和工具。
性能
两者都针对高性能进行了优化。GraphQL 的性能可能优于 REST,因为它减少了数据的过度获取和获取不足。然而,GraphQL 通常需要一个复杂的服务器端实现。因此,API 开发人员必须仔细定义模式并优化查询,以避免性能问题。
开发速度
虽然 GraphQL 更适合复杂的 API,但 REST 的简单性和在开发人员中的熟悉度可能在初期加快开发速度。
转向 GraphQL 可能涉及学习曲线。团队对每种技术的熟悉程度是一个影响因素。最终,无论使用 REST 还是 GraphQL,复杂的 API 都需要比简单 API 更多的开发时间。
文档
REST 通常依赖于第三方工具来解析 REST API 的 Open API 规范 以生成文档。然而,GraphQL 支持完整的内置文档。
了解更多:如何编写 API 文档(附示例)
缓存
REST 和 GraphQL 的服务器响应都可以被缓存。然而,缓存策略是不同的。
当客户端请求 REST API 时,API 必须指明响应是否可以缓存,以及客户端可以缓存响应多长时间。由于客户端可以在特定时间内利用缓存的数据,缓存可以通过减少 API 请求数量来提高可用性和性能。
对于GraphQL,您必须缓存单个字段,因为缓存整个GraphQL查询响应是不可行的。因此,您需要更精细的控制来有效缓存响应。因此,最好拥有一个适用于GraphQL的缓存库。
另请阅读:gRPC与REST:有什么区别?
总结
REST和GraphQL是用于构建Web API的技术。例如,Facebook创建GraphQL是为了解决REST的过度获取和获取不足的问题。GraphQL通过允许客户端发送查询来过滤所需数据并结构化响应中返回的数据,从而解决了这个问题。
虽然GraphQL解决了许多REST问题,但它不如REST为人所熟悉,并且需要一定的学习曲线。
处理复杂数据模型以及这些数据模型之间关系的API可能更适合GraphQL。相反,不涉及复杂数据的更简单的API可能更适合独立且隔离的功能。
最终,可以在REST之上抽象出GraphQL,从而获得两者的优势。