[译] 怎样使用GraphQL - 3.核心概念
英文原版地址:https://www.howtographql.com/...
在本章中,你将了解GraphQL的一些基本语言结构。 内容包括,初次认识,定义类型,查询和mutation的语法。我们还为你准备了一个基于graphql-up的沙箱环境,你可以使用它来实验学到的内容。
视图定义语言(SDL)
GraphQL有自己的类型系统,用于定义API的Schema。编写Schema的语法称为SDL。
以下是使用SDL定义简单类型Person的示例:
type Person { name: String! age: Int! }
这个Person类型有两个字段,name
和age
,是String和Int类型的,紧跟着的 ! 表示该字段是必需的。
可以在类型之间相互使用。在博客应用的例子中,一个Person
可以与一个Post
相关联:
type Post { title: String! author: Person! }
同时,另一方面,Person
类型中添加相应类型。
type Person { name: String! age: Int! posts: [Post!]! }
请注意,我们刚刚创建了Person
和Post
之间的一对多关系,因为Person
上的posts
字段实际上是一个帖子数组。
使用查询语句获取数据
使用REST API时,会从指定接口加载数据。每个接口都明确定义了返回的数据结构。这意味着客户端需要的数据,已经在URL中制定好了。
GraphQL中采用的方式截然不同。GraphQL的API通常只暴露一个接口,而不是返回固定数据结构的多个接口。 这是因为返回的数据结构不是一成不变的,而是灵活的,让客户端来决定真正需要的是什么数据。
这意味着客户端需要向服务端发送更多信息来告知所需求的数据 - 这个额外信息称为查询。
基本查询
我们来看看客户端发送给服务端的简单查询示例:
{ allPersons { name } }
在这查询中,allPersons
字段称为查询的根字段。根字段之后的所有内容称为查询的payload。此查询中,指定的唯一payload字段是name
。
此查询将返回当前存储在数据库中的所有的人员列表。返回的示例如下:
{ "allPersons": [ { "name": "Johnny" }, { "name": "Sarah" }, { "name": "Alice" } ] }
请注意,每个Person
在响应结果中只有name
一个属性,服务端并没有返回age
属性。 这正是因为name
是在查询中指定的唯一字段。
如果客户端也想要获取年龄属性,那么要做的,仅仅是调整一下查询语句,在查询的payload中加入新的字段:
{ allPersons { name age } }
GraphQL的一个重要优势是,允许直接通过嵌套的方式查询信息。例如,如果想获取一个Person
写的所有posts
,可以简单地按照类型的结构来获取数据:
{ allPersons { name age posts { title } } }
查询时带上参数
在GraphQL中,每个在视图中定义的字段都可以拥有参数(零或多个)。例如,allPersons
字段可以包含last
参数,用来返回特定数量的Persons
。相应的查询如下:
{ allPersons(last: 2) { name } }
通过Mutation操作数据
在从服务端获取到数据之后,主流的应用程序总是要更改存储在后端的数据。使用GraphQL,这些更改是使用Mutation来完成的。 Mutation一般有这三种:
创建新数据
更新现有数据
删除现有数据
Mutation遵循与查询相同的语法结构,但是它们始终需要从Mutation
关键字开始。 以下是我们如何创建一个新的Person
的例子:
mutation { createPerson(name: "Bob", age: 36) { name age } }
请注意,与之前写的查询类似,mutation也有一个根字段,上面的例子中,这个字段是createPerson
。 我们之前已经讲到过关于查询参数的内容。例子中,createPerson
接受name
和age
两个参数。
像查询一样,我们还可以为mutation指定一个payload,我们可以在其中请求新建的Person对象的其他属性。 在我们的例子中,我们指定的是name
和age
,虽然我们的例子并不是很有意义,因为我们获取的是我们已知的数据。但我们已经清晰的了解到,当请求是mutation时,也能够指定查询信息,这体现了mutation的强大之处,允许在一次请求来回中,从服务器获取新数据!
上面的mutation的响应数据是这样的:
"createPerson": { "name": "Bob", "age": "36", }
我们总是能了解到,当新增数据时,GraphQL会在服务端生成一个唯一的ID类型。扩展一下我们之前定义的Person
类型,我们可以添加一个id
字段,如下所示:
type Person { id: ID! name: String! age: Int! }
现在,当创建一个新的Person时,可以直接在mutation的payload中查询id,这是事先在客户端不知道的信息:
mutation { createPerson(name: "Alice", age: 36) { id } }
使用Subscription进行实时更新
今天,许多应用的另一个重要需求是与服务端进行实时连接,以便实时响应重要事件。针对这种场景,GraphQL提供了订阅的概念。
当客户端订阅了事件,它将与服务器建立一个稳定连接并保持住这个链接。任何时刻,当有事件触发时,服务端都会把相关信息推送到客户端。不同于查询和mutation,订阅代表的是将数据推送出去的流的模式,而不是经典的“请求 - 响应”的模式。
订阅使用的语法和查询和mutation相同。我们用来订阅Person类型上的事件,如下所示:
subscription { newPerson { name age } }
在客户端将此订阅发送到服务端之后,会建立起一个链接。之后,每当mutation被执,用来创建新的Person
时,服务端就会把有关此人的信息发送给客户端:
{ "newPerson": { "name": "Jane", "age": 23 } }
定义视图(Schema)
现在,你已经对查询,mutation和订阅有一个基本的了解,让我们将这些放在一起,学习如何编写一个视图,让你自己执行目前为止所有的示例。
视图是使用GraphQL API时最重要的概念之一。它指定了API的功能,并定义了客户端如何请求数据。它通常被视为服务器和客户端之间的协议。
通常,视图只是GraphQL类型的集合。 但是,在为API编写视图时,有一些特殊的根类型:
type Query { ... } type Mutation { ... } type Subscription { ... }
查询,mutation和订阅是客户端发送请求的入口点。要启用我们前面看到的allPersons查询,查询类型必须如下定义:
type Query { allPersons: [Person!]! }
allPersons称为API的根字段。再考虑到,我们将last
参数添加到allPersons的示例,我们必须如下定义Query:
type Query { allPersons(last: Int): [Person!]! }
类似地,对于createPerson
mutation,我们必须向Mutation类型添加一个根字段:
type Mutation { createPerson(name: String!, age: String!): Person! }
请注意,根字段有两个参数,Person的name
和age
。
最后,对于订阅,我们必须添加newPerson根字段:
type Subscription { newPerson: Person! }
将所有整合在一起,这是你在本章中看到的所有查询和mutation的完整模式:
type Query { allPersons(last: Int): [Person!]! } type Mutation { createPerson(name: String!, age: String!): Person! } type Subscription { newPerson: Person! } type Person { name: String! age: Int! posts: [Post!]! } type Post { title: String! author: Person! }