GraphQL:你能改变查询的结果吗



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

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

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

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

既然我们正在改变数据,我们肯定应该将其标记为mutation。但随后我们就失去了生成想要生成的查询的能力,因为findForum是一个查询字段,而不是突变字段。绕过这个的方法可能是重新定义突变类型中所需的所有查询字段。这显然不是一个好主意,因为您重复了大量代码,并且还使query的功能成为mutation的功能的严格子集。

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

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

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

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

editComment突变将先于deleteComment突变而消退。如果这些操作是查询,那么它们将同时运行。同样,考虑一下你是否有一个返回对象的突变,比如:

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

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

查询和突变之间的行为差异突出了为什么按照惯例,我们为每个操作定义一个突变,并避免像你的问题所建议的那样"嵌套"突变。

让你的突变更灵活的关键在于你如何将输入传递给你的突变,然后你如何在分解器中处理这些输入。不做deleteAllUserPostCommentsByForum突变,只做一个接受更强大InputType的deleteComments突变,例如:

input DeleteCommentsInput {
forumId: ID
userId: ID
}

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

实际上,你可以类似地处理创建和编辑,并保持一些干燥。例如,您的模式可能如下所示:

type Mutation {
createOrUpdateComment(comment: CommentInput)
}
input CommentInput {
id: ID
userId: ID
body: String
}

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

IMHO您失去了许多间接方面。

尝试创建"灵活"查询可能会导致服务器操作高度不优化。

查询是按结构逐级解析的,这可能会导致处理许多不必要的数据(高内存使用率)。它不能在较低的层(如sql server)上进行优化——它将导致一个天真的实现(处理),就像许多"手动启动"的sql查询与一个更复杂的有条件的查询相比。

在这种情况下,f.e.服务器根本不需要所有用户,而用户的帖子/评论通常包含user_id(和论坛/线程/帖子ID)字段——它可以直接在一个表上处理(带有已加入的帖子)。不需要整个结构只影响某些元素。

graphQL的真正力量和灵活性都放在了解析器上

请注意,删除所有或仅删除某些注释可以完全不同地实现。解析器可以选择更好的方式(通过Daniel所写的参数),但为了简单(API的可读性),最好有一个单独的突变。

最新更新