首页 > 解决方案 > GraphQL:你能改变查询的结果吗?

问题描述

在写这个问题时,我意识到我希望能够在 GraphQL 中做一些非常具体的事情,但我看不到实现它的好方法。这个想法是这样的:

GraphQL 的优点之一是它允许您进行灵活的查询。例如,如果我想在特定的每个中找到所有comments的,那么我可以进行查询postsuserforum

query{
  findForum(id:7){
    users{
      posts{
        comments{
          content
        }
      }
    }
  }
} 

这很棒。通常,您希望收集数据以对其进行变异。所以在这种情况下,也许我不想获取所有这些评论,而是想删除它们。一个天真的建议是在类型上实现一个deleteComment字段comment,它会改变调用它的对象。这很糟糕,因为请求被标记为 a query,所以它不应该改变数据。

由于我们正在改变数据,我们绝对应该将其标记为mutation. 但是随后我们失去了进行我们想要进行的查询的能力,因为findForum它是一个查询字段,而不是一个变异字段。解决此问题的一种方法可能是在突变类型中重新定义您需要的所有查询字段。这显然不是一个好主意,因为您重复了很多代码,并且还querymutation.

现在,我认为“传统”的解决方案是制作一个突变场来完成这项工作,而不是别的。因此,您定义了一个deleteAllUserPostCommentsByForum带有参数的突变字段,并以显而易见的方式实现它。但是现在你已经失去了灵活性!如果您决定要user显式查找并删除他们的所有帖子,或者如果您只想删除他们的一些帖子,则需要一个全新的突变字段。这感觉正是我认为 GraphQL 与 REST 相比有用的东西。

那么,有没有同时避免这些问题的好方法呢?

标签: graphql

解决方案


在底层,查询和突变之间唯一真正的区别是,如果单个操作包含多个突变,它们会按顺序(一次一个)而不是同时解决。查询和所有其他字段同时解析。这意味着对于这样的操作:

mutation myOperation {
  editComment(id: 1, body: "Hello!")
  deleteComment(id: 1)
}

editComment突变将在突变之前解决deleteComment。如果这些操作是查询,它们将同时运行。同样,考虑是否有一个返回对象的突变,如下所示:

mutation myOperation {
  deleteComment(id: 1) {
    id
    name
  }
}

在这种情况下,idandname字段也同时被解析(因为,即使它们作为突变的一部分返回,字段本身也不是突变)。

查询和突变之间的这种行为差异突出了为什么按照惯例我们为每个操作定义一个突变并避免像您的问题所暗示的那样“嵌套”突变。

使您的突变更加灵活的关键在于您如何将输入传递给您的突变,然后您如何在解析器中处理这些输入。不要进行deleteAllUserPostCommentsByForum突变,只需进行deleteComments接受更健壮的 InputType 的突变,例如:

input DeleteCommentsInput {
  forumId: ID
  userId: ID
}

然后,您的解析器只需要处理可能传入的输入字段的任何组合。如果您使用的是数据库,这种输入很容易转换为WHERE子句。如果您意识到您需要额外的功能,例如在某个日期之前或之后删除评论,那么您可以将这些字段添加到您的输入类型并相应地修改您的解析器——无需创建新的突变。

您实际上可以类似地处理创建和编辑,并使事情变得有点干燥。例如,您的架构可能如下所示:

type Mutation {
  createOrUpdateComment(comment: CommentInput)
}

input CommentInput {
  id: ID
  userId: ID
  body: String
}

然后,您的解析器可以检查是否包含 ID——如果是,则将操作视为更新,否则将操作视为插入。当然,在这种情况下使用非空值可能会变得很棘手(userId可能需要创建而不是更新),因此对于每种操作都有单独的输入类型是有好处的。但是,希望这仍能说明您如何利用输入类型使您的突变更加灵活。


推荐阅读