- 原文地址:GraphQL vs. REST
- 原文做者:Sashko Stubailo
- 译文出自:掘金翻译计划
- 本文永久连接:github.com/xitu/gold-m…
- 译者:wilsonandusa
- 校对者:DeadLion, steinliber
GraphQL 经常被认为是一种全新的 API 方式。你能够经过发送一次查询请求便得到所须要的数据,而不是经过服务器严格定义的请求终端。GraphQL 确实有这样的变革能力,一个团队在采用 GraphQL 后可以使得前端和后端的合做变得比以前更流畅。然而在实际操做中,两种技术都经过发送 HTTP 请求获取结果,并且 GraphQL 使用了 REST 模型中的不少内建元素前端
那么从技术层面来说它们的本质究竟是什么?这两款 API 范例的类似处和区别都有哪些?我在文章最后将会声明 GraphQL 和 REST 的区别并非很大,但 GraphQL 其自己的一些小的改变使得为开发和自定义一个 API 带来了巨大的区别。react
那么言归正传,咱们会先指出 API 的一些性质,而后咱们会讨论 GraphQL 和 REST 是如何处理它们的。android
REST 的核心理念就是资源。每一个资源都由一个 URL 定义,而后经过向指定 URL发送 GET
请求来获取资源。目前大部分 API 会获得的一个 JSON 响应。这个请求和响应以下:ios
GET /books/1
{
"title": "Black Hole Blues",
"author": {
"firstName": "Janna",
"lastName": "Levin"
}
// ... more fields here
}复制代码
注意:在以上实例中,有的 REST APIs 会把 “author” 当成独立资源返回。git
在 REST 中须要注意的是,资源的类型和你获取资源的方法是紧密相关的。当使用以上 REST 数据时,你可能会把它当成是 book 的一个终端。github
GraphQL 在这方面就至关不同了,由于在 GraphQL 里这两个概念是彻底分开的。在你的模版里可能会有 ‘Book’ 和 “author” 两种类型:数据库
type Book {
id: ID
title: String
published: Date
price: String
author: Author
}
type Author {
id: ID
firstName: String
lastName: String
books: [Book]
}复制代码
注意在这里咱们对可得到的数据类型进行了描述,但这个描述并无告诉你每一个对象是如何从客户端得到的。这就是 REST 和 GraphQL 的核心区别之一 —— 对某一指定资源的描述不必定要和获取的方式相结合。express
若是想要真正获得到某一本书或者其做者的信息,咱们须要在咱们现有的模式中创造一个 ‘Query’ 类型:编程
type Query {
book(id: ID!): Book
author(id: ID!): Author
}复制代码
如今咱们能够发送一个相似于 REST 的请求,不过此次是使用 GraphQL:后端
GET /graphql?query={ book(id: "1") { title, author { firstName } } }
{
"title": "Black Hole Blues",
"author": {
"firstName": "Janna",
}
}复制代码
很好,如今咱们有成果了!即便双方都使用 URL 来发送请求并返回相同的 JSON 结构做为回应,咱们仍是能立刻看出 GraphQL 和 REST 之间的区别。
首先,咱们能看出 GraphQL 查询的 URL 详细指出了咱们所寻找的资源以及咱们所关心的字段。并且 API 的使用者决定是否须要包括有关 ‘author’ 的资源,而不是由服务器端的代码来决定。
但最重要的是,资源的身份以及 Book 和 Author 的概念和获取的方式无关。咱们实际上可使用多种不一样的请求来获取同一本书的不一样字段。
咱们已经找到了一些类似和不一样的地方:
好吧,若是你以前使用过 GraphQL 和/或 REST的话这些看上去很基础。若是你以前没用过 GraphQL,你可使用 Launchpad 来试试这个实例 。这是一个用于在浏览器中创造和探索 GraphQL 实例的工具。
一款没法正确预测结果的 API 是没有实际用途的。当你使用一款 API 的时候,大部分状况下会把它当作程序的某一部分去使用它,这款程序会知道能够调用什么 API,以及 API 的结果是什么。这样程序才能运用好 API 返回的结果。
因此一款 API 最重要的一个特色就是去描述它到底能获得什么。你在读 API 文档的时候偏偏就是为了了解这些。如今经过使用 GraphQL 的内部描述特色或者使用相似 Swagger 这种适用于 REST 模板系统的工具,咱们能够采用编程的方式来获取这方面的信息。
目前的 REST API 一般被形容为一连串的端点:
GET /books/:id
GET /authors/:id
GET /books/:id/comments
POST /books/:id/comments复制代码
因此你能够将此 API 的“形态”描述为线性 —— 由于你能够接触一连串的信息。当你想要获取或者存储信息的时候,最早想到的问题就是“我应该使用哪个终端”?
而在 GraphQL 中,就像咱们以前提到的,你并非使用一系列 URL 来验证 API 能够得到有哪些信息,而是使用 GraphQL 的模板:
type Query {
book(id: ID!): Book
author(id: ID!): Author
}
type Mutation {
addComment(input: AddCommentInput): Comment
}
type Book { ... }
type Author { ... }
type Comment { ... }
input AddCommentInput { ... }复制代码
将它和 REST 中请求相同数据集的请求路径作对比时,有几点有趣的地方。首先,在区分读取和写入时,GraphQL 使用的是 Mutation 和 Query 这两种不一样的初始类型,而不是经过对同一 URL 发送两种不一样的 HTTP 术语。在 GraphQL 文档中,你可使用关键字来选择你所发送的操做:
query { ... }
mutation { ... }复制代码
若是想要了解更多有关查询语言的细节,请阅读我以前写的文章, “对 GraphQL 查询的分析”。
你能够看出 Query 类型中的字段和咱们以前所写的 REST 路径正好重合。这是由于此类型是咱们数据的切入点,因此这在 GraphQL 中是和终端 URL 几乎相同的一个概念。
你从 GraphQL API 中获取最初资源的方式和使用 REST 的方法相似 —— 都是经过传递一个名字和一些参数 —— 但最大的不一样之处是在这以后你会作什么。你能够用 GraphQL 发送一个复杂的请求并经过与模板之间的关系来获取额外的数据。但在 REST 中,你须要经过发送多个请求来使用相关数据去构造最初的回应,或者在 URL 中包含特殊参数来修改响应的结果。
在 REST 中,可得到数据的空间是由一系列线性的终端来描述的,而在 GraphQL 中是经过使用有关联的模板:
因为第一个类似点,不少人把 GraphQL 的 Query 类中的字段看成“终端”或者“请求”。虽然这的确是一个合理的比较,但这种理解可能会误导别人认为 Query 类和其余类的工做方式不一样,这种理解是错误的。
当你调用一款 API 的时候到底发生了什么?一般状况下 API 会在服务器端收到请求后执行一段代码。这类代码可能会进行计算,也多是从数据库中加载数据,甚至会使用另外一款 API 或作其余事。重要的是你不须要了解它在内部到底作了了什么。不过 REST 和 GraphQL 这两款 API 都具有很是标准化的内部执行方式,经过比较它们内部的执行区别,咱们能够找出这两款 API 基础层面的不一样点。
在接下来的对比中我会使用 JavaScript,由于这是我最熟悉的语言。不过你固然能够用其余语言去实现 REST 或者 GraphQL。我会省略设置服务器的步骤,由于这不是重点。
来看看这个用 experss 写的 hello world 例子,express 是 Node 里很火的 API库 之一。
app.get('/hello', function (req, res) {
res.send('Hello World!')
})复制代码
咱们首先建立了一个可以返回hello world字串符的/hello 终端。经过这个例子中咱们能够得知使用 REST API 来写服务器时一个 HTTP 请求的生命周期:
GraphQL 的工做方式极为类似,对于同一个 hello world 的例子来讲二者几乎相同:
const resolvers = {
Query: {
hello: () => {
return 'Hello world!';
},
},
};复制代码
就像你所看到的,咱们将函数和一个类别中的字段相呼应,为指定的 URL 提供一个处理函数。在这个例子中,‘hello’ 是 ‘Query’ 中的一个字段。在 GraphQL 中,这种对字段进行操做的函数被称为 resolver。
咱们须要用 Query 来发送请求:
query {
hello
}复制代码
当服务器接收到 GraphQL 的请求会执行如下步骤:
因此你最终获得的结果为:
{ "hello": "Hello, world!" }复制代码
但这里有个小技巧,咱们实际上能够连续访问字段两次!
query {
hello
secondHello: hello
}复制代码
在这个例子中出现了相同的生命周期,但因为咱们使用化名对同一个字段发送了两次请求,hello 的 resolver 实际上被使用了两次。这个例子很牵强,但重点是咱们能够对同一请求中对多个字段进行操做,并且在一个 query 中咱们也能够对单个字段进行屡次使用。
为了进行补充,如下是一个嵌套在一块儿的 resolvers 例子:
{
Query: {
author: (root, { id }) => find(authors, { id: id }),
},
Author: {
posts: (author) => filter(posts, { authorId: author.id }),
},
}复制代码
这些 resolvers 能够用来对 query 进行补充:
query {
author(id: 1) {
firstName
posts {
title
}
}
}复制代码
因此即便这些 resolvers 是平级的,因为它们能够和多种类型相结合,你能够在嵌套的 query 里将这些 resolvers 连在一块儿使用。若是想了解 GraphQL 是如何执行工做的,请阅读如下文章“详解 Graph QL”。
图解:对资源进行获取的 REST 屡次请求 vs GraphQL 的一次请求
最终咱们能够得知,REST 和 GraphQL API 均可以在网络中经过不一样方式使用函数。若是你对如何搭建 REST API 很熟悉,那么使用 GraphQL API 应该不会很不同。不过 GraphQL 有很大的优点,由于你可使用它去执行多个相关函数,并且全程不须要屡次请求往返。
总而言之,你能够将 GraphQL 当成是能够在一次请求里执行多个终端的系统,就像是重复使用的 REST。
咱们没法在此文章中对全部细节作出诠释,好比对象识别、超媒体以及缓存。我之后可能会再讨论这些问题,但我想让你明白的是,经过了解 API 的基本知识点可得知,REST 和 GraphQL 工做时所使用的基础观念是十分类似的。
我以为二者之间的区别反而成为了 GraphQL 的优点。特别是给予使用者构建多个 resolver 函数的功能很是炫酷,并且也能够发送一个复杂的请求来一次性获得多种资源,整个过程是可预测的。这个特色避免了 API 的使用者为了构建某个回应形式而去使用多个终端,同时也避免了处理额外不须要的数据。
然而,GraphQL 目前尚未 REST 那么多的工具和扩展。比方说,你没法对 GraphQL 的结果使用 HTTP 的缓存方式。但目前社区方面正在努力打造更好的工具和框架,并且你可使用相似 Apollo client 和 Relay 这类缓存工具。
若是有更多有关对比 REST 和 GraphQL 的想法,请积极留言!
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、React、前端、后端、产品、设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划。