首页 > 解决方案 > 通过字段在 GraphQL 中定义突变:这是不好的做法吗?

问题描述

假设你有一个user类型,而 auser有很多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字段 forPostType和一个findUser突变字段(通常是一个查询字段)。假设这些字段的定义方式很明显,然后我会进行查询

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

这是一个坏主意吗?

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

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

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
        }
      }
    }
  }
}

我想我真正想做的是使用查询查找一些数据,然后改变我找到的数据。我不知道如何在 GraphQL 中做到这一点。

第二次编辑:这个问题似乎有一个定义明确的组成部分,我在这里问过。事实证明,其中一个问题可以回答另一个问题,并且可以关闭,但我不知道哪种方式还可以。

标签: graphql

解决方案


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

来自https://graphql.org/learn/queries/的引述如下:

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

这是一个很好的约定,因为它使维护、测试和调试更容易。副作用,无论是否有意,都很难追踪和理解。特别是如果您在 GraphQL 查询中使用它们,这可能是任意大且复杂的。没有什么可以阻止您同时查询和修改同一个对象及其兄弟对象,并通过简单的嵌套在一个查询中多次执行此操作。这很容易出错。

即使你成功了,代码的可读性和可维护性也会受到影响。例如,如果您知道只有您的突变修改了数据,并且查询对其没有影响,您将立即知道从哪里开始寻找特定行为的实现。推理您的程序通常如何工作也容易得多。

如果您只编写小的、正确命名的、细粒度的突变,那么您可以比如果您有一个在不同点更新不同数据的复杂查询更容易推断出它们所做的事情。

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

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


编辑

好的,所以我知道您将在哪里进行此操作-您希望将 GraphQL 查询的灵活性赋予突变。当然,这个特定的例子会起作用。不走这条路只会是关于未来deletePost如果这是您将定义的唯一操作,那么讨论这个是没有意义的。

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

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

这些都是未来的潜在问题(可能还有更多)。如果这些不关心你,那么请继续实施。我个人会逃避一个这样做的项目,但如果你真的很聪明,我看不出有任何东西会在技术上完全阻止你实现你想要的:]


推荐阅读