【译】GraphQL vs. REST

两种经过 HTTP 发送数据的方式:区别在哪里?

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 的概念和获取的方式无关。咱们实际上可使用多种不一样的请求来获取同一本书的不一样字段。

总结

咱们已经找到了一些类似和不一样的地方:

  • 相同: 都拥有资源这个概念,并且均可以指定资源的身份
  • 相同: 都能经过 HTTP GET 和一个 URL 来获取信息
  • 相同: 请求的返回值都是 JSON 数据
  • 不一样: 在 REST 中,你所访问的终端就是所需对象的身份,在 GraphQL 中,对象的身份和获取的方式是独立存在的
  • 不一样: 在 REST 中,资源的形式和大小是由服务器所决定的。在 GraphQL 中,服务器声明哪些资源能够得到,而客户端会对其所需资源做出请求。

好吧,若是你以前使用过 GraphQL 和/或 REST的话这些看上去很基础。若是你以前没用过 GraphQL,你可使用 Launchpad 来试试这个实例 。这是一个用于在浏览器中创造和探索 GraphQL 实例的工具。

URL 路径 vs 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 中是经过使用有关联的模板:

  • 相同: REST API 中的一列终端和 GraphQL API 中的 Query 和 Mutation 类的字段很像,都是数据的切入点。
  • 相同: 两种 API 均可以区分数据的读取和写入。
  • 不一样: 在 GraphQL 中,你可使用由模板定义的关系,经过发送一次请求从初始点一直走到相关数据。然而在 REST 中,你必需要使用多个终端来获取相关资源。
  • 不一样: 在 GraphQL 中,除了在每一个请求的根源处所能获取的类型都是 Query 类外,Query 的字段和其余类的字段没有本质区别。比方说,你能够在 Query 的每一个字段里放一个参数。而在 REST 中,嵌套的URL里没有第一类这个概念。
  • 不一样: 在 REST 中,你经过将 HTTP 术语 GET 改成 POST 来指定写入,但在 GraphQL 里须要改变请求里的关键字

因为第一个类似点,不少人把 GraphQL 的 Query 类中的字段看成“终端”或者“请求”。虽然这的确是一个合理的比较,但这种理解可能会误导别人认为 Query 类和其余类的工做方式不一样,这种理解是错误的。

路径处理器 vs Resolvers

当你调用一款 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 请求的生命周期:

  1. 服务器接收请求并解析 HTTP 术语 (这个例子中术语为 ‘GET’)和其 URL
  2. API 库将术语和路径相结合并在服务器代码中找到与之相匹配的函数
  3. 函数运行并返回结果
  4. API 库将结果序列化与响应代码和数据头相结合,最终发送给客户端

GraphQL 的工做方式极为类似,对于同一个 hello world 的例子来讲二者几乎相同:

const resolvers = {
  Query: {
    hello: () => {
      return 'Hello world!';
    },
  },
};复制代码

就像你所看到的,咱们将函数和一个类别中的字段相呼应,为指定的 URL 提供一个处理函数。在这个例子中,‘hello’ 是 ‘Query’ 中的一个字段。在 GraphQL 中,这种对字段进行操做的函数被称为 resolver

咱们须要用 Query 来发送请求:

query {
  hello
}复制代码

当服务器接收到 GraphQL 的请求会执行如下步骤:

  1. 服务器接收请求并开始解析 GraphQL 的请求
  2. 此 Query 的每一个字段会被仔细分析来找出有哪些 resolver 函数会被使用
  3. 函数运行并返回结果
  4. GraphQL 库和服务器将返回结果和回应相结合,最终获得和 Query 形态相匹配的结果

因此你最终获得的结果为:

{ "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 有很大的优点,由于你可使用它去执行多个相关函数,并且全程不须要屡次请求往返。

  • 相同: REST的终端和 GraphQL 的字段都会在服务器端运行函数
  • 相同: 二者本质上都须要依靠框架和库来使用和处理网络模板。
  • 不一样: 在 REST 中,每次请求一般只使用一个路径处理函数。在 GraphQL 中,同一 Query 可使用多个 resolver 来使用多个资源创造嵌套在一块儿的回应。
  • 不一样: 在 REST 中,你能够本身创造每一个回应的形式。在 GraphQL 中,回应的模式经过 GraphQL 的执行库来与请求的形式相匹配。

总而言之,你能够将 GraphQL 当成是能够在一次请求里执行多个终端的系统,就像是重复使用的 REST。


这些意味着什么?

咱们没法在此文章中对全部细节作出诠释,好比对象识别、超媒体以及缓存。我之后可能会再讨论这些问题,但我想让你明白的是,经过了解 API 的基本知识点可得知,REST 和 GraphQL 工做时所使用的基础观念是十分类似的。

我以为二者之间的区别反而成为了 GraphQL 的优点。特别是给予使用者构建多个 resolver 函数的功能很是炫酷,并且也能够发送一个复杂的请求来一次性获得多种资源,整个过程是可预测的。这个特色避免了 API 的使用者为了构建某个回应形式而去使用多个终端,同时也避免了处理额外不须要的数据。

然而,GraphQL 目前尚未 REST 那么多的工具和扩展。比方说,你没法对 GraphQL 的结果使用 HTTP 的缓存方式。但目前社区方面正在努力打造更好的工具和框架,并且你可使用相似 Apollo clientRelay 这类缓存工具。

若是有更多有关对比 REST 和 GraphQL 的想法,请积极留言!


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOSReact前端后端产品设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划

相关文章
相关标签/搜索