在底层,查询和突变之间的唯一真正区别是,如果单个操作包含多个突变,它们将按顺序(一次一个)而不是同时解析。查询和所有其他字段同时解析。这意味着对于这样的操作:
mutation myOperation {
editComment(id: 1, body: "Hello!")
deleteComment(id: 1)
}
The editComment
突变将在之前解决deleteComment
突变。如果这些操作是查询,那么它们将同时运行。同样,考虑一下是否有一个返回对象的突变,如下所示:
mutation myOperation {
deleteComment(id: 1) {
id
name
}
}
在这种情况下,id
and name
字段是also同时解决(因为,即使它们作为突变的一部分返回,字段本身也不是突变)。
查询和突变之间的行为差异凸显了为什么按照惯例,我们为每个操作定义一个突变,并避免像您的问题所建议的那样“嵌套”突变。
使突变更加灵活的关键在于如何将输入传递给突变,以及如何在解析器中处理这些输入。而不是制作一个deleteAllUserPostCommentsByForum
突变,只需做一个deleteComments
接受更稳健的输入类型的突变,例如:
input DeleteCommentsInput {
forumId: ID
userId: ID
}
然后,您的解析器只需要处理可能传入的输入字段的任何组合。如果您使用数据库,这种输入很容易转换为WHERE
条款。如果您意识到需要其他功能,例如删除特定日期之前或之后的评论,则可以将这些字段添加到您的输入类型并相应地修改您的解析器 - 无需创建新的突变。
实际上,您可以类似地处理创建和编辑,并让事情变得更干燥。例如,您的架构可能如下所示:
type Mutation {
createOrUpdateComment(comment: CommentInput)
}
input CommentInput {
id: ID
userId: ID
body: String
}
然后,您的解析器可以检查是否包含 ID - 如果包含,则它将该操作视为更新,否则它将将该操作视为插入。当然,在这种情况下使用非空值可能会变得很棘手(userId
创建可能需要,但更新不需要),因此对于每种操作都有单独的输入类型有话要说。然而,希望这仍然说明了如何利用输入类型来使您的突变更加灵活。