接上篇 —— 如何使用 GraphQL-基础教程:GraphQL 比 REST 的优点 —— 继续翻译How to GraphQL 系列教程。
基础和进阶系列翻译已完成:
在本章中,将学习 GraphQL 的一些基本语言构造。包括对定义 types 以及发送 queries 和 mutations 语法的第一印象。
模式定义语言(Schema Definition Language,SDL)
GraphQL 有自己的类型系统,该系统用于定义 API 的 _schema_。编写 schema 的语法称为模式定义语言(Schema Definition Language,SDL)。
这是一个示例,说明如何使用 SDL 定义一个简单的类型,称为 Person
:
1 | type Person { |
此类型有两个 字段(fields)_,它们分别称为name
和 age
,分别为 String
和 Int
类型。类型后面的 !
表示此字段是 _必需 的。
也可以表达类型之间的关系。在 博客 应用程序的示例中,Person
可以与 Post
关联:
1 | type Post { |
相反,关系的另一端需要放在 Person
类型上:
1 | type Person { |
注意,由于在 Person
和 Post
之间创建了 一对多(one-to-many) 关系,因为 Person
上的 posts
字段实际上是 post _数组_。
带查询条件获取数据
使用 REST API 时,将从特定端点加载数据。每个端点都有一个明确定义的返回信息结构。这意味着客户端的数据要求实际上是在其连接的 URL 中 编码(encoded) 的。
GraphQL 中采用的方法完全不同。 GraphQL API 并不具有返回固定数据结构的多个端点,而是通常仅公开 _一个端点_。之所以有效的原因是返回的数据的结构并不固定。相反,它是完全灵活的,可以让客户端决定实际需要什么数据。
这意味着客户端需要向服务器发送更多的 信息 来表达其数据需求 ,此信息称为 query(查询) 。
基本查询
让我们看一个客户端可以发送到服务端的查询示例:
1 | { |
该查询中的 allPersons
字段称为查询的 根字段(root field)_。根字段之后的所有内容都称为查询的 _负载(payload) 。该查询的负载中指定的唯一字段是 name
。
该查询将返回当前存储在数据库中的所有人员的列表。下面是一个示例响应(response):
1 | { |
请注意,每个人在响应中仅具有 name
,服务端不会返回 age
。这就是因为 name
是查询中唯一指定的字段。
如果客户端还需要人员的 age
,则只需稍微调整查询,并将新字段包括在查询的负载中即可:
1 | { |
GraphQL 的主要优点之一是可以自然地查询嵌套(nested)信息。例如,如果想加载 Person
写的所有 posts
,则可以简单地按照类型的结构来请求此信息:
1 | { |
带参数查询
在 GraphQL 中,可以在 schema 中指定每个 字段 ,使其具有任意多个参数。例如,allPersons
字段可以具有 last
参数,使响应中仅返回特定数目的人员。下面是对应查询的结构:
1 | { |
使用 mutation 写数据
除了从服务端请求信息之外,大多数应用程序还需要某种方式来更改当前存储在服务端中的数据。使用 GraphQL,这些更改是使用 变更(mutation) 进行的。通常存在三种 变更
:
- 创建新的数据
- 修改已存在的数据
- 删除已存在的数据
变更遵循与查询相同的语法结构,但是它们始终需要以 mutation
关键字开头。下面是一个有关如何创建新的 Person
的示例:
1 | mutation { |
注意,与我们之前写的查询类似,变更也有一个 _根字段_。在本例中为 createPerson
。我们也已经了解了字段参数的概念。在本例中 createPerson
字段采用两个参数来指定新人员的 name
和 age
。
像查询一样,我们还可以为变更指定负载,在变更中我们可以请求新的 Person
对象的不同属性。在本例中,我们请求了name
和 age
,尽管在本例中并没有多大帮助,因为将它们传递给 mutation
时,我们显然已经知道它们是什么。但是,发送 mutation
时也可以查询信息是一个非常强大的特性,使你可以在一次往返中从服务端检索新信息!
上述变更的服务端响应如下所示:
1 | "createPerson": { |
经常会发现, GraphQL 类型具有唯一的 ID ,它们是在创建新对象时由服务端生成的。扩展一下之前的Person
类型,可以添加一个id
,如下所示:
1 | type Person { |
现在,当创建一个新的 Person
时,因为这是客户端之前没有 id
,所以可以直接在变更的负载中请求 id
:
1 | mutation { |
使用订阅来实时更新
当今,许多应用的另一个重要要求是与服务端建立 “实时” 连接,以便立即获取重要事件。对于这种情况,GraphQL 提供了 订阅(subscription) 的概念。
客户端订阅事件时,它将启动并保持与服务器的稳定连接。每当该特定事件实际发生时,服务端就会将相应的数据推送到客户端。与遵循典型的“请求-响应循环”的查询和变更不同,订阅表示发送到客户端的数据的 _流(stream)_。
订阅使用与查询和变更相同的语法编写。下面是一个示例,订阅了 Person
类型发生的事件:
1 | subscription { |
客户端将此订阅发送到服务器后,它们之间将打开连接。然后,每当执行一个新的变更以创建一个新的 Person
时,服务端就会将有关此人员的信息发送给客户端:
1 | { |
定义一个 Schema
现在已经对查询,变更和订阅有了基本的了解,下面将它们放在一起,并学习如何编写 schema 以执行到目前为止所看到的示例。
schema 是使用 GraphQL API 时最重要的概念之一。它指定 API 的功能并定义客户端如何请求数据。通常将其视为服务端和客户端之间的 _合约_。
通常,schema 只是 GraphQL 类型的集合。但是,在为 API 编写 schema 时,有一些特殊的 根(root) 类型:
1 | type Query { ... } |
Query
、Mutation
和 Subscription
类型是客户端发送的请求的 入口点(entry point) 。为了实现之前看到的 allPersons
查询,必须将 Query
类型编写如下:
1 | type Query { |
allPersons
被称为 API 的 _根字段(root field)_。再考虑到 allPersons
字段添加 last
参数的示例,我们必须编写 Query
,如下所示:
1 | type Query { |
同样,对于 createPerson
变更,必须在 Mutation
类型中添加一个根字段:
1 | type Mutation { |
注意,这个根字段也接受两个参数,即新的 Person
的 name
和 age
。
最后,对于订阅,我们必须添加 newPerson
根字段:
1 | type Subscription { |
综上所述,这是在本章中看到的所有查询和变更的 完整 schema:
1 | type Query { |
学习更多
要了解更多有关 GraphQL 核心概念的内容,可以查看以下系列文章:
- GraphQL Server Basics (Part I): GraphQL Schemas, TypeDefs & Resolvers Explained
- GraphQL Server Basics (Part II): The Network Layer
- GraphQL Server Basics (Part III): Demystifying the
info
argument in GraphQL resolvers
前端记事本,不定期更新,欢迎关注!