0%

如何使用GraphQL-进阶教程:更多概念

接上篇 —— 如何使用 GraphQL-进阶教程:服务端 —— 继续翻译How to GraphQL 系列教程

学习 GraphQL 的进阶概念,比如片段、查询参数、别名、接口和更多的 SDL 特性。

基础和进阶系列翻译已完成:

  1. 介绍
  2. GraphQL 比 REST 的优点
  3. 核心概念
  4. 架构
  5. 客户端
  6. 服务端
  7. 更多概念
  8. 工具和生态
  9. 安全
  10. 常见问题

通过片段增强可复用性

片段(Fragments) 是一个方便的功能,有助于改善 GraphQL 代码的结构和可复用性。片段是特定类型字段的集合。

假设我们具有以下类型:

1
2
3
4
5
6
7
8
type User {
name: String!
age: Int!
email: String!
street: String!
zipcode: String!
city: String!
}

在这里可以将与用户的真实地址有关的所有信息表示为一个片段:

1
2
3
4
5
6
fragment addressDetails on User {
name
street
zipcode
city
}

接下来在编写查询以访问用户的地址信息时,可以使用以下语法引用该片段,实际拼出了四个字段:

1
2
3
4
5
{
allUsers {
...addressDetails
}
}

这个查询与下述代码等同:

1
2
3
4
5
6
7
8
{
allUsers {
name
street
zipcode
city
}
}

对字段进行参数化

在 GraphQL 类型定义中,每个字段可以包含任意多个 参数(argument) 。与强类型编程语言传递给函数的参数类似,每个参数都必须具有 名称(name)类型(type) 。在 GraphQL 中,也可以为参数指定 _默认值(default values)_。

作为示例,让我们看一下开始的 schema 的一部分:

1
2
3
4
5
6
7
8
type Query {
allUsers: [User!]!
}

type User {
name: String!
age: Int!
}

可以在 allUsers 字段中添加一个参数,该参数允许我们传递参数以过滤出特定年龄段的用户。还指定一个默认值,以便默认情况下将返回所有用户:

1
2
3
type Query {
allUsers(olderThan: Int = -1): [User!]!
}

可以使用以下语法将这个 olderThan 参数传递到查询中:

1
2
3
4
5
6
{
allUsers(olderThan: 30) {
name
age
}
}

对查询结果使用别名

GraphQL 的主要优势之一是可以在单个请求中发送多个查询。但是由于响应数据是在请求的字段结构之后形成的,因此当发送多个询问相同字段的查询时,可能会遇到命名问题:

1
2
3
4
5
6
7
8
{
User(id: "1") {
name
}
User(id: "2") {
name
}
}

实际上这会使 GraphQL 服务器产生错误,因为它们只是参数不同的相同字段。发送这样的查询的唯一方法是使用别名,即为查询结果指定名称:

1
2
3
4
5
6
7
8
{
first: User(id: "1") {
name
}
second: User(id: "2") {
name
}
}

服务器将根据指定的别名为每个User对象命名:

1
2
3
4
5
6
7
8
{
"first": {
"name": "Alice"
},
"second": {
"name": "Sarah"
}
}

进阶 SDL

SDL 提供了之前章节中未讨论的几种语言功能。在下文中,将通过实例进行讨论。

对象 和 标量类型

在 GraphQL 中,有两种不同的类型。

  • 标量(Scalar) 类型代表数据的具体单位。GraphQL 规范具有五个预定义的标量:如 StringIntFloatBooleanID

  • 对象(Object) 类型具有 _字段(field)_,它们表示该类型的属性且可组合。对象类型的示例是我们在上一节中看到的 UserPost 类型。

在每个 GraphQL schema 中,都可以定义自己的标量和对象类型。自定义标量中经常被引用的例子是 Date 类型,其中实现需要定义如何校验、序列化和反序列化该类型。

枚举

GraphQL 允许定义 枚举(enum) 类型,这是一种语言功能,用于表达具有一组固定值的语义类型。因此,我们可以定义一个名为 Weekday 的类型来表示一周中的所有天:

1
2
3
4
5
6
7
8
9
enum Weekday {
MONDAY
TUESDAY
WEDNESDAY
THURSDAY
FRIDAY
SATURDAY
SUNDAY
}

注意,从技术上讲枚举是特殊的标量类型。

接口

接口(interface) 可用于以抽象方式描述类型。允许您指定实现接口时,任何具体类型都需要具有的一组字段。在许多 GraphQL schema 中,每种类型都必须具有一个 id 字段。可以通过此字段定义一个接口,然后确保所有自定义类型都必须实现该接口:

1
2
3
4
5
6
7
8
9
interface Node {
id: ID!
}

type User implements Node {
id: ID!
name: String!
age: Int!
}

联合类型

联合类型(Union type) 可以用来表示一个类型应该是其他类型集合中的一个。通过示例可以最好地理解它们。如下:

1
2
3
4
5
6
7
8
9
type Adult {
name: String!
work: String!
}

type Child {
name: String!
school: String!
}

现在,我们可以将 Person 类型定义为 AdultChild 的 _联合_:

1
union Person = Adult | Child

这就带来了一个不同的问题:在 GraphQL 查询中,我们要求检索有关 Child 的信息,但仅具有 Person 类型的信息,如何知道是否可以实际访问此字段?

答案是 _有条件片段(conditional fragment)_:

1
2
3
4
5
6
7
8
9
10
11
{
allPersons {
name # works for `Adult` and `Child`
... on Child {
school
}
... on Adult {
work
}
}
}

前端记事本,不定期更新,欢迎关注!


👆 全文结束,棒槌时间到 👇