GraphQL中的突变和订阅
在本文中,我们将研究在 GraphQL 中使用 Mutation 和 Subscription 类型来操作和观察数据的变化,而不仅仅是查询。 随意在 官方文档 中发现更多信息。
为简单起见,我们不会使用任何数据库或 HTTP 请求,但了解如何使用 模式和解析器 设置基本 API 是必要的。
安装
我们将使用 graphql-yoga 库来设置我们的服务器和 nodemon 让它自动重新加载。 我们还需要像 Prepros 或 babel 这样的预处理器,这样我们就可以使用 JavaScript 的最新功能。
$ npm i graphql-yoga nodemon
样板设置
除了我们的服务器设置,我们只有一个空的 users 数组和一个简单的模式和解析器,用于返回我们的所有用户。
服务器.js
import { GraphQLServer } from 'graphql-yoga'
const users = [];
const typeDefs = `
type Query {
users: [User!]!
}
type User {
name: String!
age: Int!
}
`;
const resolvers = {
Query: {
user() {
return users;
}
}
}
const server = new GraphQLServer({ typeDefs, resolvers });
server.start(() => console.log('server running'));
我们需要一个 start 脚本,它会在我们的输出文件上运行 nodemon:
包.json
{
"name": "graphql-api",
"version": "1.0.0",
"description": "",
"main": "server.js",
"dependencies": {
"graphql-yoga": "^1.16.7"
},
"devDependencies": {
"nodemon": "^1.19.1"
},
"scripts": {
"start": "nodemon server-dist.js"
},
"author": "",
"license": "ISC"
}
现在在终端中你可以运行 npm run start。
在 localhost:4000 上,我们应该让 GraphQL Playground 启动并运行,查询 user { name } 返回我们的空数组。
创建突变
我们的突变的语法与我们的查询几乎相同。 我们只需要声明我们想要的选项,添加任何参数(如果有的话),并声明完成后应该返回什么类型。
为了组织起见,通常将数据分解为自己的特殊类型,称为 input 类型,而不是内联添加所有 out 参数。 这是您将在 Prisma 之类的工具中看到的通用命名约定,用于将输入命名为解析器以单词 input 结尾的任何内容,因此 addUser 获得 AddUserInput 输入。
服务器.js
const typeDefs = `
type Mutation {
addUser(data: AddUserInput): User!
}
input AddUserInput {
name: String!,
age: Int!
}
`;
就像查询一样,我们可以访问 args 上的参数并将我们的新用户添加到我们的数组中,然后返回它们。
const resolvers = {
Query: {...},
Mutation: {
addUser(parent, args, ctx, info) {
const user = { ...args.data };
users.push(user);
return user;
}
}
}
删除和更新突变
由于语法如此简单,几乎可以毫不费力地充实其他 CRUD 操作。
通过按名称搜索用户,我们将知道我们正在删除或更新哪个项目。
服务器.js
const typeDefs = `
type Mutation {
deleteUser(name: String!): User!
updateUser(name: String!, data: UpdateUserInput): User!
}
input UpdateUserInput {
name: String
age: Int
}
`
const resolvers = {
Query: { ... },
Mutation: {
deleteUser(parent, args, ctx, info) {
// We're just finding the index of the user with a matching name,
// checking if it exists, and removing that section of the array.
const userIndex = users.findIndex(user => user.name.toLowerCase() === args.name.toLowerCase());
if (userIndex === -1) throw new Error('User not found');
const user = users.splice(userIndex, 1);
return user[0];
},
updateUser(parent, args, ctx, info) {
const user = users.find(user => user.name.toLowerCase() === args.who.toLowerCase());
if (!user) throw new Error('User not found');
// This way, only the fields that are passed-in will be changed.
if (typeof args.data.name === "string") user.name = args.data.name;
if (typeof args.data.age !== "undefined") user.age = args.data.age;
return user;
}
}
}
现在在 localhost:4000 你可以尝试这个突变并再次查询我们的数组。
mutation {
addUser(data: {
name: "Alli",
age: 48
}) {
name
age
}
}
或在另一个选项卡中:
mutation {
updateUser(name: "Alli", data: {
name: "Crusher",
age: 27
}) {
name
age
}
}
订阅
我们可以使用特殊的 Subscription 类型来让我们观察数据的任何变化。 语法与查询和突变的语法非常相似,只需添加类型 Subscription,添加您希望它观看的任何内容,以及您想要返回的内容。 我们将返回一个自定义类型,它将向我们发送回我们更改的数据,并告诉我们它是创建、删除还是更新操作。
要使用订阅,我们需要使用 graphql-yoga 中的 PubSub 并在其他所有操作之前对其进行初始化。 在我们的订阅解析器中,我们将使用一个名为 subscribe 的函数,它需要返回一个异步事件,我们将其命名为 user。 每当我们想将某些东西连接到此订阅时,我们将使用此事件名称。
服务器.js
import { GraphQLServer, PubSub } from 'graphql-yoga';
const pubsub = new PubSub();
const typeDefs = `
type Subscription {
user: UserSubscription!
}
type UserSubscription {
mutation: String!
data: User!
}
`
const resolvers = {
Query: { ... },
Mutation: { ... },
Subscription: {
user: {
subscribe() {
return pubsub.asyncIterator('user');
}
}
}
}
我们的订阅本身在生成的 GraphQL 文档中已设置并可用,但它不知道何时触发或返回什么。 回到我们的突变中,我们将添加 pubsub.publish 以链接到我们的 user 事件并将我们的数据传回。
const resolvers = {
Query: { ... },
Mutation: {
addUser(parent, args, ctx, info) {
const user = { ...args.data };
users.push(user);
// We'll just link it to our user event,
// and return what type of mutation this is and our new user.
pubsub.publish("user", {
user: {
mutation: "Added",
data: user
}
});
return user;
},
deleteUser(parent, args, ctx, info) {
const userIndex = users.findIndex(
user => user.name.toLowerCase() === args.who.toLowerCase()
);
if (userIndex === -1) throw new Error("User not found");
const user = users.splice(userIndex, 1);
pubsub.publish("user", {
user: {
mutation: "Deleted",
data: user[0]
}
});
return user[0];
},
updateUser(parent, args, ctx, info) {
const user = users.find(
user => user.name.toLowerCase() === args.who.toLowerCase()
);
if (!user) throw new Error("User not found");
if (typeof args.data.name === "string") user.name = args.data.name;
if (typeof args.data.age !== "undefined") user.age = args.data.age;
pubsub.publish("user", {
user: {
mutation: "Updated",
data: user
}
});
return user;
}
},
Subscription: { ... }
};
在 localhost:4000 我们可以打开一个新选项卡并运行以下订阅。 您应该会看到带有小旋转轮的“正在听……”消息。 在另一个选项卡中,我们现在可以运行我们过去的任何其他突变,我们的订阅将自动返回已完成的内容和更改的内容。
subscription {
user {
mutation
data {
name
age
}
}
}
结论
我希望这有助于理解如何使用 Mutations 和 Subscriptions 设置 GraphQL API。 如果在设置时有任何问题,您可以随时查看 this repo。