0%

Apollo入门引导(一):构建schema

接上篇 —— Apollo 入门引导(零):介绍 —— 继续翻译 Apollo 的官网入门引导

为你的数据图创建蓝图。

Apollo 入门引导 - 目录:

  1. 介绍
  2. 构建 schema
  3. 连接数据源
  4. 编写查询解析器
  5. 编写变更解析器
  6. 连接 Apollo Studio
  7. 创建 Apollo 客户端
  8. 通过查询获取数据
  9. 通过变更修改数据
  10. 管理本地状态

完成时间:15 分钟

每个数据图都使用 schema 来定义其包含的数据类型。例如,在线书店的 schema 可能包括以下类型:

1
2
3
4
5
6
7
8
9
10
11
# 一本book有一个title和一个author
type Book {
title: String
author: Author
}

# 一个author有一个name和一个书单(list)
type Author {
name: String
books: [Book]
}

在下面的步骤中,将设置一个 GraphQL 服务器用来强制 schema 的结构,然后我们来定义 schema 本身。

启动 Apollo 服务

仅当数据图符合该 schema 的结构时,该 schema 才生效。强制 schema 的结构是 Apollo 服务 的核心功能之一,Apollo 服务是一种可用于生产环境的开源库,可帮助实现数据图的 API。

start/server 目录中安装 Apollo 服务(以及项目的其他依赖项):

1
cd start/server && npm install

和 Apollo 服务一起启动时所需要的两个包是 apollo-servergraphql ,这两个包也是通过上述命令一起安装的。

现在,导航到 src/index.js 并使用它创建服务。将以下代码粘贴到文件中:

1
2
3
4
const { ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');

const server = new ApolloServer({ typeDefs });

该代码从 apollo-server 导入 ApolloServer 类,并从 src/schema.js 导入 schema(当前还未定义)。然后,它创建一个 ApolloServer 的新实例,并通过 typeDefs 属性将导入的 schema 传递给实例。

现在,Apollo 服务已准备好接收 schema,接下来对 schema 进行定义。

定义 schema 的类型

GraphQL schema 定义了客户端可以读取和写入数据图的数据类型。schema 是强类型的,所以会有强大的开发工具。

schema 的结构应该支持客户端将要执行的操作。我们的示例应用需要有以下操作:

  • 获取所有即将发射的火箭清单
  • 通过 ID 获取特定的发射详情
  • 用户登录
  • 为已登录的用户预订火箭发射
  • 取消已登录用户先前预订的火箭发射

让我们一起来设计 schema 使上述操作更直接。

src/schema.js 中,从 apollo-server 中导入 gql,并为 schema 创建一个名为 typeDefs 的变量:

1
2
3
4
5
6
7
const { gql } = require('apollo-server');

const typeDefs = gql`
# 从这里开始写你的schema
`;

module.exports = typeDefs;

该 schema 将进入 gql 函数内部(反引号之间的内容)。我们用于编写 schema 的语言是 GraphQL 的模式定义语言(SDL)。

由于该 schema 直接位于应用程序客户端和底层数据服务之间,因此前端和后端团队应在其结构上协作。当开发自己的数据图时,请进行schema 优先(schema-first)开发模式,先在 schema 上达成一致,再开始实现 API。

对象类型

GraphQL schema 中的大多数定义是 对象类型(object type)。定义的每种对象类型都应代表一个应用程序客户端可能需要交互的对象。

例如,示例应用需要能够获取即将到来的火箭发射列表,因此我们应该定义 Launch 类型以支持该操作。

将以下内容粘贴到 src/schema.jstypeDefs 声明的反引号中:

1
2
3
4
5
6
7
type Launch {
id: ID!
site: String
mission: Mission
rocket: Rocket
isBooked: Boolean!
}

Launch对象类型具有 字段(field) 的集合,并且每个字段都有其自己的类型。字段的类型可以是对象类型或标量类型(scalar type)。标量类型可解析为单个值的原始值(例如IDStringBooleanInt)。除了 GraphQL 内置的标量类型,还可以定义自定义标量类型

声明的字段类型后的感叹号()表示 “此字段的值不能为空”。

在上面的 Launch 定义中,MissionRocket是指 其他 对象类型。让我们为它们添加定义,同样包括 User 类型(都写在反引号内):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type Rocket {
id: ID!
name: String
type: String
}

type User {
id: ID!
email: String!
trips: [Launch]!
}

type Mission {
name: String
missionPatch(size: PatchSize): String
}

enum PatchSize {
SMALL
LARGE
}

如果声明的字段的类型在[方括号]中,则它是指定类型的 数组(array) 。如果数组后面有一个感叹号,则该数组不能为 null,但可以为空数组。

请注意, Mission 类型的 missionPatch字段有一个名为 size 的参数,该参数是枚举类型(enum type)PatchSize。当查询带有参数的字段时,该字段的值会根据提供的参数值而有所不同。在这种情况下,为 size 提供的值将确定返回任务相关徽章(patch)的大小(SMALLLARGE)。

查询类型

我们已经定义了数据图中存在的对象,但是客户端还无法 获取(fetch) 这些对象。为了解决这个问题,我们的 schema 需要定义“查询(query)”,客户端可以针对数据图执行该“查询”。

可以将数据图支持的查询定义为特殊类型的字段,称为 Query 类型。将以下内容粘贴到 schema 定义中:

1
2
3
4
5
type Query {
launches: [Launch]!
launch(id: ID!): Launch
me: User
}

Query类型定义了三个可供客户端执行的查询:launcheslaunchme

  • launches 查询将返回所有即将发射的 Launch 数组。

  • launch 查询将返回一个与给定 id 参数相对应的 Launch

  • me 查询将返回当前登录的 User 的详细信息。

Mutation 类型

查询可以使客户端获取数据,但不能 修改(modify) 数据。为了使客户端能够修改数据,schema 需要定义一些 更改(mutations)

Mutation 类型是一种特殊类型,其结构与 Query 类型类似。将以下内容粘贴到 schema 定义中:

1
2
3
4
5
type Mutation {
bookTrips(launchIds: [ID]!): TripUpdateResponse!
cancelTrip(launchId: ID!): TripUpdateResponse!
login(email: String): String # login token
}

这个 Mutation 类型定义了三种可供客户端执行的可用变更:bookTripscancelTriplogin

  • bookTrips 更改可以使已登录的用户预订 Launch 的旅程(由发射 ID 数组指定)。
  • cancelTrip 更改使登录用户可以取消他们先前预订的旅程。
  • login 更改使用户可以通过 email 地址来登录。

bookTripscancelTrip 更改返回相同的对象类型:TripUpdateResponse。更改的返回类型完全取决于你,但是建议为更改响应定义专门的特殊对象类型。

TripUpdateResponse 的定义添加到 schema:

1
2
3
4
5
type TripUpdateResponse {
success: Boolean!
message: String
launches: [Launch]
}

此响应类型包含 success 状态,对应的 message 以及由该更改修改的 Launch 序列。优秀的实践是,使用更改返回其修改的所有对象,以便发出请求的客户端可以实时更新自己的缓存和 UI,不必再进行一次后续查询。

示例应用的 schema 现已完成!

运行服务

返回到 src/index.js 并调用 server.listen(),如下所示:

1
2
3
4
5
6
7
8
const { ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');

const server = new ApolloServer({ typeDefs });

server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});

保存后,运行 npm start 来启动服务!🎉 Apollo 服务运行在端口 4000 上。

探索你的 schema

现在服务正在运行,可以使用开发者工具来 内省(introspect) 其 schema 类型和字段。稍后还将从这些工具运行测试查询。

内省是一种有用的 GraphQL 功能,能够获取用于开发的服务端 schema。对于在生产环境中运行的服务器,应当禁用(disabled)该选项。如果将环境变量 NODE_ENV 设置为 production,则 Apollo 服务会自动禁用内省功能。

使用 Apollo Studio(推荐)

Apollo Studio Explorer 是一个功能强大的免费 Web IDE,用于浏览 GraphQL schema 并针对它构建查询:

如果想将 Explorer 与本地运行的服务一起使用,可以在 Apollo Studio 中创建 dev graph,如下所示:

  1. 访问studio.apollographql.com/dev
    如果还未创建一个 Apollo 帐户,则系统会提示(同样,Apollo 帐户和 Explorer 是完全免费的)。

  2. 在显示的表单中,为 graph 指定名称,并指定本地服务的 URL(本教程为http://localhost:4000):

  1. 点击 Create graph 。Studio 会自动打开 Explorer 以与你的服务一起使用!

如果想在 Explorer 中内省服务器的 schema,请单击左侧的 “documentation” 选项卡:

显示了 schema 的根类型(QueryMutation)。可以单击任何类型以查看其字段,然后进入 schema,会看到 shcema 的类型和字段与提供给服务器的内容是匹配的。

了解有关 Apollo Studio Explorer 的更多信息

使用 GraphQL Playground

GraphQL Playground是用于查询 GraphQL 服务的开源工具。它提供的功能比 Apollo Studio Explorer 少,但是同样也可以完成本教程所需的一切内容。每个 Apollo 服务在安装时同样也会安装这个工具。

在服务运行时,在浏览器中访问 localhost:4000 以打开 GraphQL Playground。要自检服务器的 schema,请单击右侧的 Schema 按钮:

显示了 schema 的查询,更改以及对象类型定义:

我们的服务现在知道自己支持哪些 GraphQL 类型和操作,但是它不知道从何处获取数据以响应那些操作。接下来,我们将服务连接到两个数据源。


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


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