Declan's Blog

Hi, nice to meet you.

  1. 1. 什么是 GraphQL
  2. 2. GraphQL 的特征
  3. 3. Type
    1. 3.1. Scalar Type
    2. 3.2. Object Type
    3. 3.3. Type Modifier
  4. 4. Schema
    1. 4.1. Query
    2. 4.2. Resolver
    3. 4.3. 优点
    4. 4.4. 缺点

什么是 GraphQL

GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。

一个简单的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 描述数据
type Project {
name: String
tagline: String
contributors: [User]
}

// 请求所要的数据
{
project(name: "GraphQL") {
tagline
}
}

// 得到可预测的结果
{
"project": {
"tagline": "A query language for APIs"
}
}

GraphQL 的特征

  • 可描述性的:使用 GraphQL,你获取的都是你想要的数据,不多也不会少
  • 分级性的:GraphQL 天然遵循对象间的关系,通过一个简单的请求,我们可以获取到一个对象及其相关的对象,比如说,通过一个简单的请求,我们可以获取一个作者和他创建的所有文章,然后可以获取文章的所有评论
  • 强类型的:使用 GraphQL 的类型系统,我们可以描述能够被服务器查询的可能的数据,然后确保从服务器获取到的数据和我们查询的一致
  • 不做语言限制:并不绑定于某一特定的语言,实际上现在已经有一些不同的语言有了实践;
  • 兼容于任何后台:GraphQL 不限于某一特定数据库,可以使用已经存在的数据,代码,甚至可以连接第三方的 APIs
  • 好反省的:GraphQL 服务器能够查询架构的细节

Type

对于数据模型的抽象是通过 Type 来描述的,每一个 Type 有若干 Field 组成,每个 Field 又分别指向某个 Type。

GraphQL 的 Type 简单可以分为两种,一种叫做 Scalar Type(标量类型),另一种叫做 Object Type(对象类型)。

Scalar Type

GraphQL 中的内建的标量包含,String、Int、Float、Boolean、Enum,可以通过 Scalar 声明一个新的标量,标量是 GraphQL 类型系统中最小的颗粒。

Object Type

对象类型用来抽象一些复杂的数据模型

1
2
3
4
5
6
7
8
9
10
11
type User {
id: ID
name: String
}

type Article {
id: ID
text: String
isPublished: Boolean
author: User
}

Type Modifier

类型修饰符有两种,分别是 List 和 Required ,它们的语法分别为[Type]和 Type!, 同时这两者可以互相组合,比如[Type]!或者[Type!]或者[Type!]!,它们的含义分别为:

  • 列表本身为必填项,但其内部元素可以为空
  • 列表本身可以为空,但是其内部元素为必填
  • 列表本身和内部元素均为必填

Schema

Schema 是用来描述对于接口获取数据逻辑的,我们不妨把它当做 REST 架构中每个独立资源的 uri 来理解它,只不过在 GraphQL 中,我们用 Query 来描述资源的获取方式。因此,我们可以将 Schema 理解为多个 Query 组成的一张表。

Query

下面三种基本查询类型是作为 Root Query(根查询)存在的,对于传统的 CRUD 项目,我们只需要前两种类型就足够了,第三种是针对当前日趋流行的 real-time 应用提出的。

我们按照字面意思来理解它们就好,如下:

  • query(查询):当获取数据时,应当选取 Query 类型
  • mutation(更改):当尝试修改数据时,应当使用 mutation 类型
  • subscription(订阅):当希望数据更改时,可以进行消息推送,使用 subscription 类型

以 REST 和 GraphQL 的角度举例:

Rest 接口

1
2
3
4
5
GET /api/v1/articles/
GET /api/v1/article/:id/
POST /api/v1/article/
DELETE /api/v1/article/:id/
PATCH /api/v1/article/:id/

GraphQL Query

1
2
3
4
5
6
7
8
9
10
query {
articles(): [Article!]!
article(id: Int): Article!
}

mutation {
createArticle(): Article!
updateArticle(id: Int): Article!
deleteArticle(id: Int): Article!
}

Resolver

Resolver(解析函数)用来提供相关 Query 所返回数据的逻辑。GraphQL 中,Query 和与之对应的 Resolver 是同名的。

Resolver 本身的声明在各个语言中是不一样的,因为它代表数据获取的具体逻辑。它的函数签名(以 js 为例子)如下:

1
2
3
function(parent, args, ctx, info) {
...
}

其中的参数的意义如下:

  • parent: 当前上一个 Resolver 的返回值
  • args: 传入某个 Query 中的函数(比如上面例子中 article(id: Int)中的 id)
  • ctx: 在 Resolver 解析链中不断传递的中间变量(类似中间件架构中的 context)
  • info: 当前 Query 的 AST 对象

Resolver 内部实现对于 GraphQL 完全是黑盒状态。这意味着 Resolver 如何返回数据、返回什么样的数据、从哪返回数据,完全取决于 Resolver 本身,基于这一点,在实际中,往往把 GraphQL 作为一个中间层来使用,数据的获取通过 Resolver 来封装,内部数据获取的实现可能基于 RPC、REST、WS、SQL 等多种不同的方式。同时,在对一些未使用 GraphQL 的系统进行迁移时(比如 REST),可以很好的进行增量式迁移。

优点

  • 网络开销低,可以在单一请求中获取 REST 中使用多条请求获取的资源
  • 强类型 Schema(约束意味着可以根据规范形成文档、IDE、错误提示等生态工具)
  • 特别适合图状数据结构的业务场景(比如好友、流程、组织架构等系统)

缺点

  • 本身的语法相比较 REST 和 RPC 均复杂一些
  • 实现方面需要配套 Caching 以解决性能瓶颈
  • 对于 API 的版本控制当前没有完善解决方案(社区的建议是不要使 API 版本化)
  • 仍然是新鲜事物,很多技术细节仍然处于待验证状态

参考资料

  • GraphQL
  • RPC vs REST vs GraphQL
  • 一篇文章帮你理清 GraphQL 的核心概念(译)
  • GraphQL 规范
This article was last updated on days ago, and the information described in the article may have changed.