通过字段定义GraphQL中的突变:这是不好的做法吗?

时间:2018-09-13 16:35:22

标签: graphql

假设您有一个user类型,而一个user有很多posts。然后想象您想找到一个用户,并删除其所有帖子。一种方法是实现以下mutation字段:

field deleteAllPosts, types[Types::PostType] do
  argument :user_id, types.String

  resolve -> (obj,args,ctx){ 
    posts = Posts.where(user_id:args[:user_id])
    posts.each{|post| post.destroy}
  }
end

然后查询

mutation {
  deleteAllPosts(user_id:1)
}

将删除ID为1的用户的所有帖子。

在执行此操作之前,我曾想过要以不同的方式进行此操作,但我从未见过其他人这样做过。我想检查一下这种不同的方式没有任何陷阱,或者是我不应该使用它的原因。

其想法是改为在deletePost上放置一个PostType字段,并在突变上放置一个findUser字段(通常是查询字段)。假设很明显如何定义这些字段,然后我进行查询

mutation{
  findUser(id:1){
    posts{
      deletePost{
      id
      }
    }
  }
}

这是个坏主意吗?

根据反馈进行编辑:我担心的一件事是,用户可以原则上可以在内部进行deletePost选择查询。但是我很想说那是“他们的错”。我想 说“只有在突变查询中才能进行此选择”,但是我认为这在GraphQL中是不可能的。

为了避免XY问题,这就是为什么我热衷于使用这种想法,而不是最初的想法。感觉更具表现力(换句话说,感觉不那么多余)。假设一段时间后,您决定要删除属于特定posts的那些users的所有group。然后在我认为的“惯例”中,您应该创建一个全新的突变字段:

field deleteAllPostsInGroup, types[Types::PostType] do
  argument :group_id, types.String

  resolve -> (obj,args,ctx){ 
    posts = Group.find_by(args[:group_id]).users.map{|u| u.posts}.flatten
    posts.each{|post| post.destroy}
  }
end

在我建议的约定中,您仅定义了一个琐碎的findGroup字段(但您必须在不属于突变的突变中对其进行定义),然后进行查询:

mutation{
  findGroup(id:1){
    users{
      posts{
        deletePost{
        id
        }
      }
    }
  }
}

我想我真正想做的是使用 query 查找一些数据,然后变异我找到的数据< / strong>。我不知道如何在GraphQL中执行此操作。

第二编辑::这个问题似乎有一个明确定义的组成部分,我已经问过here。事实证明,其中一个问题可以回答另一个问题,可以解决,但我不知道该怎么回事。

1 个答案:

答案 0 :(得分:0)

这基本上是一个代码质量问题,类似于询问DRY原理或封装的要点。

https://graphql.org/learn/queries/中的一句话是:

  

在REST中,任何请求都可能最终导致服务器上出现一些副作用,但按照惯例,建议不要使用GET请求来修改数据。 GraphQL是类似的-从技术上讲,任何查询都可以实现以引起数据写入。但是,建立一个约定是很有用的,该约定应使引起写操作的任何操作都应通过突变显式发送。

这是一个很好的约定,因为它使维护,测试和调试更加容易。无论是有意还是无意,副作用都很难追踪和理解。尤其是在GraphQL查询中包含它们时,查询可能会非常大且复杂。没有什么可以阻止您同时查询和修改同一个对象及其同级对象,并通过简单的嵌套在一个查询中多次执行此操作。这很容易弄错。

即使您将它拉开,代码的可读性和可维护性也会受到影响。例如。如果您知道只有您的变异曾经修改过数据,并且查询对它没有影响,那么您将立即知道从哪里开始寻找特定行为的实现。通常来说,您的程序的工作原理也更容易推断。

如果您只编写名称正确的细小突变,那么比起复杂查询在不同点更新不同数据的情况,您可以更轻松地推断出它们的作用。

最后但并非最不重要的一点是,如果您需要将工作转移给其他人,则遵循约定很有用。

简而言之-一切都是为了让自己和他人的生活在未来更加轻松。


编辑

好的,所以我知道您要进行的工作-您希望为变异提供GraphQL查询的灵活性。当然,这个特定的例子会起作用。不采用这种方式只会导致未来。如果deletePost是您将要定义的唯一操作,则没有必要讨论。

如果不是这种情况,那么如果要删除5条特定的用户帖子怎么办?您是否会为findGroup提供额外的参数,然后将其传递给树?但是,为什么findGroup方法必须知道您将如何处理其结果?这种违背了灵活查询本身的想法。如果您还想对用户执行变异该怎么办? findGroup的更多参数?如果可以通过其他方式查询用户和帖子,例如按域划分的用户,按类别分类的帖子等,该怎么办?在那里也定义相同的参数吗?您如何确保每次操作(尤其是一次执行几个操作)时,所有关系链接都已正确删除到数据库中?您将必须想象查询和查询突变的每种可能组合,并为它们适当地编写代码。由于查询大小不受限制,因此最终可能很难做到。即使单个查询突变(deletePost)的目的很明确且易于掌握,但整个 query 都不是。很快,即使对于您来说,您的查询也会变得太复杂以至于无法理解,您可能会开始将其分解为较小的查询,这只会进行特定的突变。这样,您就可以回到原始约定,但是要返回一个更复杂的版本。您可能最终也会定义一些常规突变。您将如何更新或添加帖子?那将使您的逻辑无处不在。

如果您正在编写突变,则不会出现这些问题。为了获得更好的可维护性,需要做更多的工作。

这些都是将来的潜在问题(可能还有更多)。如果这些与您无关,请继续执行。我个人会避免这样做的一个项目,但是如果您真的很聪明,我看不到任何技术上会完全阻止您实现所需目标的东西:]