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。