接上篇 —— Apollo 入门引导(三):编写查询解析器 —— 继续翻译 Apollo 的官网入门引导。
学习 GraphQL 的变更是如何修改数据的。
Apollo 入门引导 - 目录:
完成时间:10 分钟
上一篇已经编写了 schema 字段中查询操作所需的所有解析器。现在继续为 schema 的变更编写解析器。这个过程基本是相同的。
登录
首先为 Mutation.login 编写一个解析器,使用户可以登录到应用程序。将以下内容添加到解析器映射中的 Query 字段下面:
// Query: {
// ...
// },
Mutation: {
login: async (_, { email }, { dataSources }) => {
const user = await dataSources.userAPI.findOrCreateUser({ email });
if (user) return Buffer.from(email).toString('base64');
}
},
该解析器获取一个 email 地址,并返回对应用户实体的登录 token。如果该 email 地址不存在对应的用户实体,则会创建一个。
在下一章中将学习如何在客户端上保存此登录 token。
验证登录用户
示例应用中使用的身份验证方法不是完全安全的,生产环境中不应使用。但是可以将以下示例的原理应用于到 安全 的基于 token 的身份验证方法中。
Mutation.login 解析器返回一个 token,客户端可以在服务中使用该 token 进行身份验证。现在需要向服务添加用于身份验证逻辑。
在 src/index.js 中,导入 isEmail 函数,并将 context 函数传给 ApolloServer 的构造函数:
const isEmail = require('isemail');
const server = new ApolloServer({
context: async ({ req }) => {
// 每次请求时的简单身份验证
const auth = (req.headers && req.headers.authorization) || '';
const email = Buffer.from(auth, 'base64').toString('ascii');
if (!isEmail.validate(email)) return { user: null };
// email通过email查找用户
const users = await store.users.findOrCreate({ where: { email } });
const user = (users && users[0]) || null;
return { user: { ...user.dataValues } };
},
// 可选的构造函数选项
});
客户端发送到我们服务的每一次 GraphQL 操作,都会被调用一次上面定义的 context 函数。该函数的返回值成为 context 参数,该参数传给和对应操作一起运行的每个解析器。
下面是 context 函数的作用:
- 获取传入请求中包含的
Authorization请求头(如果有)的值。 - 解码
Authorization请求头的值。 - 如果解码后的值是
email地址,则从数据库中获取该电子邮件地址对应用户详细信息,并在user字段中返回包含这些详细信息的对象。
通过在每个操作执行开始时创建此 context 对象,所有的解析器都可以访问已登录用户的详细信息,并针对该用户执行特定的操作。
bookTrips 和 cancelTrip
下面将 bookTrips 和 cancelTrip 的解析器添加到 Mutation 对象中:
//Mutation: {
// login: ...
bookTrips: async (_, { launchIds }, { dataSources }) => {
const results = await dataSources.userAPI.bookTrips({ launchIds });
const launches = await dataSources.launchAPI.getLaunchesByIds({
launchIds,
});
return {
success: results && results.length === launchIds.length,
message:
results.length === launchIds.length
? '成功预定旅程'
: `下面的发射不能被预定: ${launchIds.filter(
id => !results.includes(id),
)}`,
launches,
};
},
cancelTrip: async (_, { launchId }, { dataSources }) => {
const result = await dataSources.userAPI.cancelTrip({ launchId });
if (!result)
return {
success: false,
message: '取消旅程失败',
};
const launch = await dataSources.launchAPI.getLaunchById({ launchId });
return {
success: true,
message: '旅程已取消',
launches: [launch],
};
},
为了匹配 schema,这两个解析器都返回一个符合 TripUpdateResponse 类型结构的对象。此类型的字段包括 success 指示器,状态 message 和预定或者取消变更执行后的 launches 数组。
bookTrips解析器需要考虑**部分成功(partial success)**的可能性,其中某些发射被成功预订,而另一些失败了。。上面message字段里的代码表示部分成功。
测试变更
现在已经准备好测试我们的变更!重新启动服务,然后在浏览器中打开到 Apollo Studio Explorer 或 GraphQL Playground。
获取登录 token
GraphQL 变更的结构与查询完全相同,只不过使用的是 mutation 关键字。粘贴下面变更的代码并运行:
mutation LoginUser {
login(email: "daisy@apollographql.com")
}
服务返回的响应如下:
"data": {
"login": "ZGFpc3lAYXBvbGxvZ3JhcGhxbC5jb20="
}
下面的字符串是登录的 token(仅仅只是提供的电子邮件地址的 Base64 编码)。复制它给下一个变更操作使用。
ZGFpc3lAYXBvbGxvZ3JhcGhxbC5jb20=
预定行程
现在来尝试预订一些旅行。因为仅允许通过身份验证的用户预订行程,所以将在请求中包括登录 token。
首先,将下面的变更粘贴到工具的查询编辑器中:
mutation BookTrips {
bookTrips(launchIds: [67, 68, 69]) {
success
message
launches {
id
}
}
}
接下来,将以下内容粘贴到工具的 Headers 面板中:
{
"authorization": "ZGFpc3lAYXBvbGxvZ3JhcGhxbC5jb20="
}
运行变更,应该会看到一条成功 message,以及刚刚预订的旅程的 id 数组。
在 GraphQL Playground 中手动运行变更是测试 API 的很有帮助的方式,但是真实的应用需要其他工具来确保其数据图安全地添加和更改。在下一部分中,我们将服务连接到 Apollo Studio 工具。
前端记事本,不定期更新,欢迎关注!