作者:迈迈最love | 来源:互联网 | 2022-12-04 19:25
在写这个问题时,我意识到我想在GraphQL中做一些非常具体的事情,而我看不到实现它的好方法。这个想法是这样的:
GraphQL的优点之一是它允许您进行灵活的查询。例如,如果我想找到所有comments
的所有posts
每一个user
在特定的forum
话,我可以让查询
query{
findForum(id:7){
users{
posts{
comments{
content
}
}
}
}
}
太好了 通常,您希望收集数据以使其发生变异。因此,在这种情况下,也许我不想获取所有这些注释,而是想删除它们。幼稚的建议是deleteComment
在comment
类型上实现一个字段,该字段会使被调用的对象发生变化。这很糟糕,因为该请求被标记为query
,因此它不应更改数据。
由于我们正在对数据进行变异,因此我们绝对应该将其标记为mutation
。但是随后,我们失去了进行所需查询的能力,因为findForum
它是查询字段,而不是突变字段。解决此问题的一种方法可能是在突变类型内重新定义您需要的所有查询字段。显然,这不是一个好主意,因为您需要重复很多代码,并且还会使功能成为该功能query
的严格子集mutation
。
现在,我认为“常规”解决方案是创建一个可以完成此任务的突变字段,而无需执行其他任何操作。因此,您定义了一个deleteAllUserPostCommentsByForum
带参数的变体字段,并以明显的方式实现它。但是现在您失去了灵活性!如果您改为决定要user
显式查找并删除其所有帖子,或者只想删除其某些帖子,则需要一个全新的突变字段。尽管与REST相比,GraphQL对于我来说很有用,但这确实像是一种事情。
那么,有没有一种好的方法可以同时避免这些问题?
1> Daniel Reard..:
在幕后,查询和突变之间的唯一真正区别是,如果单个操作包含多个突变,则它们将按顺序(一次一个)解决,而不是同时解决。查询和所有其他字段会同时解析。这意味着对于这样的操作:
mutation myOperation {
editComment(id: 1, body: "Hello!")
deleteComment(id: 1)
}
该editComment
突变将之前解决deleteComment
突变。如果这些操作是查询,则它们将同时运行。同样,请考虑是否有返回对象的变异,如下所示:
mutation myOperation {
deleteComment(id: 1) {
id
name
}
}
在这种情况下,id
and name
字段也会同时解析(因为即使它们作为突变的一部分返回,但这些字段本身也不是突变)。
查询和突变之间在行为上的差异突出说明了为什么按照惯例我们会为每个操作定义一个突变,并避免像您的问题所建议的那样“嵌套”突变。
使变异更加灵活的关键在于如何将输入传递给变异,以及随后如何在解析器中处理这些输入。除了进行deleteAllUserPostCommentsByForum
突变之外,只需进行deleteComments
接受更强大的InputType 的突变即可,例如:
input DeleteCommentsInput {
forumId: ID
userId: ID
}
然后,您的解析器只需要处理可能传入的输入字段的任何组合即可。如果您使用的是db,则这种输入很容易转换为WHERE
子句。如果您意识到需要其他功能,例如在某个日期之前或之后删除注释,则可以将这些字段添加到“输入类型”并相应地修改解析器-无需创建新的突变。
实际上,您可以类似地处理创建和编辑,并使内容变干一些。例如,您的架构可能如下所示:
type Mutation {
createOrUpdateComment(comment: CommentInput)
}
input CommentInput {
id: ID
userId: ID
body: String
}
然后,您的解析器可以检查是否包含ID-如果包含ID,则它将操作视为更新,否则将操作视为插入。当然,在这种情况下使用非null userId
可能会比较棘手(创建可能需要,但更新可能不需要),因此对于每种操作具有不同的输入类型,要说些什么。但是,希望这仍能说明您如何利用输入类型来使您的突变更加灵活。