GraphQL中的突变和订阅

来自菜鸟教程
跳转至:导航、​搜索

在本文中,我们将研究在 GraphQL 中使用 MutationSubscription 类型来操作和观察数据的变化,而不仅仅是查询。 随意在 官方文档 中发现更多信息。

为简单起见,我们不会使用任何数据库或 HTTP 请求,但了解如何使用 模式和解析器 设置基本 API 是必要的。

安装

我们将使用 graphql-yoga 库来设置我们的服务器和 nodemon 让它自动重新加载。 我们还需要像 Preprosbabel 这样的预处理器,这样我们就可以使用 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