通过字段定义GraphQL中的突变:这是不好的做法吗? [英] Defining mutations in GraphQL via fields: Is this bad practice?

查看:41
本文介绍了通过字段定义GraphQL中的突变:这是不好的做法吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设您具有 user 类型,并且 user 具有许多 posts .然后想象您想找到一个用户,并删除他们的所有帖子.一种方法是实现以下 mutation 字段:

Suppose you have a user type, and a user has many posts. Then imagine you want to find a user, and delete all of their posts. One way to do this is to implement the following mutation field:

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的用户的所有帖子.

will delete all the posts of the user with id 1.

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

Before I did this, I thought about doing it a different way, which I've not seen anyone else do. I wanted to check that this different way doesn't have any pitfalls, or reasons I shouldn't use it.

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

The idea is to instead put a deletePost field for PostType, and a findUser field on mutation (which would typically be a query field). Assuming it's obvious how those fields would be defined, I would then make the query

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

这是个坏主意吗?

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

Edit in response to feedback: One thing I'm concerned about is the possibility that a user could, in principle, make the deletePost selection inside of a query. But I'm tempted to say that that's "their fault". I'd like to say "this selection can only be made if it is inside of a mutation query", but I don't think that's possible in GraphQL.

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

In order to avoid the XY problem, here is why I am keen to use this idea rather than the initial one. It feels more expressive (said differently, it feels less redundant). Suppose that, after a while, you decide that you want to delete all the posts for those users belonging to a particular group. Then in what I regard as the 'convention', you should create a whole new mutation field:

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 字段(但您必须在不属于突变的突变中对其进行定义),然后进行查询:

whereas in my suggested convention you just define a trivial findGroup field (but you have to define it on mutation, where it doesn't belong), and make the query:

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

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

I suppose that really what I'm trying to do is use a query to find some data, and then mutate the data I've found. I don't know how to do this in GraphQL.

第二次:这个问题似乎有一个明确定义的组件,我已经问过

Second It seems like there is a well-defined component of this question, which I have asked here. It may turn out that one of these questions answers the other, and can be closed, but I don't know which way round yet.

推荐答案

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

This is basically a code quality issue and is similar to asking about the point of the DRY principle or encapsulation.

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

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

In REST, any request might end up causing some side-effects on the server, but by convention it's suggested that one doesn't use GET requests to modify data. GraphQL is similar - technically any query could be implemented to cause a data write. However, it's useful to establish a convention that any operations that cause writes should be sent explicitly via a mutation.

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

This is a good convention as it makes maintenance, testing and debugging easier. Side-effects, whether intentional or not, can be awfully difficult to track and understand. Particularly if you have them in GraphQL queries, which can be arbitrarily large and complex. There is nothing preventing you from querying and modifying the same object and it's siblings at the same time, and doing this multiple times in one query by simple nesting. It is very easy to get this wrong.

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

Even if you pull it off, code readability and maintainability suffer. E.g. if you knew that only your mutations ever modified the data, and queries had no effect on it, you would immediately know where to start looking for the implementation of a particular behaviour. It is also a lot easier to reason about how your program works in general.

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

If you only write small, properly named, granular mutations, you can reason about what they do more easily than you could if you had a complex query which updated different data at different points.

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

Last but not necessarily least, sticking to conventions is useful if you ever need to transfer your work to someone else.

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

In short - it is all about making the lives of yourself and others easier in the future.

编辑

好的,所以我知道您要进行的工作-您想让GraphQL查询的灵活性赋予突变.当然,这个特定的例子会起作用.不采用这种方式只会与未来有关.如果 deletePost 是您将要定义的唯一操作,则没有必要进行讨论.

OK, so I see where you are going with this - you want to give the flexibility of a GraphQL query to the mutations. Sure, this particular example would work. Not going this way would only be about the future. There is no point in discussing this if deletePost is the only operation you will ever define.

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

If that's not the case, then what if you wanted to delete, let's say, 5 specific user posts? Would you give extra parameters to findGroup and then pass those down the tree? But then why does findGroup method have to know about what you will do with it's results? That kind of defies the idea of a flexible query itself. What if you also wanted to perform mutations on users? More parameters for findGroup? What if users and posts can be queried in a different way, like, users by domains, posts by categories, etc.? Define the same parameters there too? How would you ensure that with every operation (especially if you do a few of them at once) all the relational links are properly erased in your database? You would have to imagine every possible combination of queries and query-mutations and code appropriately for them. Since query size is unlimited it could end up being very hard to do. And even if the purpose of an individual query-mutation (deletePost) is clear and easy to grasp, the overall query would not be. Quickly your queries would become too complex to understand even for you and you'd probably begin breaking them down to smaller ones, which would only do specific mutations. This way you'd go back to the original convention but a more complex version of it. You would probably also end up defining some regular mutations too. How would you update or add posts, e.g.? That would spread your logic all over the place.

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

These questions would not occur if you were writing mutations. That's slightly more work in exchange for better maintainability.

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

These are all potential issues in the future (and there are probably more). If these don't concern you, then please go ahead with the implementation. I personally would run away from a project that did this, but if you are really clever, I don't see anything that would technically completely prevent you from achieving what you want :]

这篇关于通过字段定义GraphQL中的突变:这是不好的做法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆