突变方法是否需要在顶层?[英] Are mutation methods required to be on the top level?

本文是小编为大家收集整理的关于突变方法是否需要在顶层?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

所有文档和教程通常显示出看起来像这样的突变的简单示例:

extend type Mutation {
  edit(postId: String): String
}

但是,这种方式edit方法必须在所有实体中都是唯一的,对我来说,这似乎不是写东西的一种非常强大的方式.我想描述突变类似于我们描述查询的方式,类似的东西:

type PostMutation {
  edit(postId: String): String
}

extend type Mutation {
  post: PostMutation
}

这似乎是一个有效的架构(它编译,我可以看到它反映在生成的Graph-i-QL文档中).但是我找不到使解析器使用此模式的方法.

这是GraphQl的支持案例?

推荐答案

这是可能的,但通常不是一个好主意,因为:

它从http://spec.graphql.org/june2018/#sec-normal-and-serial-serial-ectution"中打破了规格. >第6.3.1节:

由于除顶级突变字段以外的其他字段的分辨率必须始终是无副作用和愿意的,因此执行顺序不得影响结果,因此服务器可以自由按任何顺序执行字段条目认为最佳.

换句话说,突变类型上的字段应具有副作用,例如CRUD操作.

将突变放在概念上是有意义的.您正在采取的任何操作(喜欢帖子,验证电子邮件,提交订单等)并不依赖GraphQl必须在采取动作之前解决其他字段.这与您实际查询数据时不同.例如,要在帖子上获取评论,我们可能必须解决user字段,然后是a posts字段,然后最后每个帖子的comments字段.在每个"级别"下,字段的内容取决于父字段所解决的值.突变通常不是这种情况.

在引擎盖下,突变被顺序解析.这与并行发生的正常场分辨率相反.这意味着,例如,A User类型的firstName和lastName同时解决.但是,如果您的操作类型为mutation,则根部将一次解决一个.因此,在这样的查询中:

mutation SomeOperationName {
  createUser
  editUser
  deleteUser
}

每个突变都会按照文档中出现的顺序进行一次.但是,这仅适用于根部,并且仅在操作为a mutation时,因此这三个字段将并行解析:

mutation SomeOperationName {
  user {
    create
    edit
    delete
  }
}

如果您仍然想这样做,尽管以上是这样,这是您在使用makeExecutableSchema时做到的,这是阿波罗在引擎盖下使用的方法:

const resolvers = {
  Mutation: {
    post: () => ({}), // return an empty object,
  },
  PostMutation: {
    edit: () => editPost(),
  },
  // Other types here
}

您的模式定义为PostMutation作为对象类型,因此GraphQl期望该字段返回对象.如果您省略了post的解析器,它将返回null,这意味着不会触发返回类型的解析器(PostMutation).这也意味着,我们也可以写:

mutation {
  post
}

除了仍然是有效的查询之外,什么也没做.这是避免这种模式结构的另一个原因.

其他推荐答案

绝对不同意丹尼尔!

这是一种了不起的方法,有助于前人员仔细了解哪些操作具有一种或另一种资源/模型.并且不要列出突变的looo列表.

在一个请求中调用多个突变是常见的反图案.对于这种情况,最好创建一个复杂的突变.

但是,即使您需要使用多个突变进行此类操作,您也可以使用别名:

await graphql({
  schema,
  source: `
  mutation {
    op1: article { like(id: 1) }
    op2: article { like(id: 2) }
    op3: article { unlike(id: 3) }
    op4: article { like(id: 4) }
  }
`,
});

expect(serialResults).toEqual([
  'like 1 executed with timeout 100ms',
  'like 2 executed with timeout 100ms',
  'unlike 3 executed with timeout 5ms',
  'like 4 executed with timeout 100ms',
]);

请参阅以下测试案例: https://github.com/nodkz/conf-talks/blob/master/articles/graphql/schema-design/测试/strong>/mutations-test.js /p>

类似/不同的方法是异步与超时,并顺序起作用

本文地址:https://www.itbaoku.cn/post/1938023.html

问题描述

All docs and tutorials usually show simple examples of mutations that look like this:

extend type Mutation {
  edit(postId: String): String
}

But this way the edit method has to be unique across all entities, which to me seems like not a very robust way to write things. I would like to describe mutation similar to how we describe Queries, something like this:

type PostMutation {
  edit(postId: String): String
}

extend type Mutation {
  post: PostMutation
}

This seems to be a valid schema (it compiles and I can see it reflected in the generated graph-i-ql docs). But I can't find a way to make resolvers work with this schema.

Is this a supported case for GraphQL?

推荐答案

It's possible but generally not a good idea because:

It breaks the spec. From section 6.3.1:

Because the resolution of fields other than top‐level mutation fields must always be side effect‐free and idempotent, the execution order must not affect the result, and hence the server has the freedom to execute the field entries in whatever order it deems optimal.

In other words, only fields on the mutation root type should have side effects like CRUD operations.

Having the mutations at the root makes sense conceptually. Whatever action you're doing (liking a post, verifying an email, submitting an order, etc.) doesn't rely on GraphQL having to resolve additional fields before the action is taken. This is unlike when you're actually querying data. For example, to get comments on a post, we may have to resolve a user field, then a posts field and then finally the comments field for each post. At each "level", the field's contents are dependent on the value the parent field resolved to. This normally is not the case with mutations.

Under the hood, mutations are resolved sequentially. This is contrary to normal field resolution which happens in parallel. That means, for example, the firstName and lastName of a User type are resolved at the same time. However, if your operation type is mutation, the root fields will all be resolved one at a time. So in a query like this:

mutation SomeOperationName {
  createUser
  editUser
  deleteUser
}

Each mutation will happen one at a time, in the order that they appear in the document. However, this only works for the root and only when the operation is a mutation, so these three fields will resolve in parallel:

mutation SomeOperationName {
  user {
    create
    edit
    delete
  }
}

If you still want to do it, despite the above, this is how you do it when using makeExecutableSchema, which is what Apollo uses under the hood:

const resolvers = {
  Mutation: {
    post: () => ({}), // return an empty object,
  },
  PostMutation: {
    edit: () => editPost(),
  },
  // Other types here
}

Your schema defined PostMutation as an object type, so GraphQL is expecting that field to return an object. If you omit the resolver for post, it will return null, which means none of the resolvers for the returning type (PostMutation) will be fired. That also means, we can also write:

mutation {
  post
}

which does nothing but is still a valid query. Which is yet another reason to avoid this sort of schema structure.

其他推荐答案

Absolutely disagree with Daniel!

This is an amazing approach which helps to frontenders fastly understand what operations have one or another resource/model. And do not list loooong lists of mutations.

Calling multiple mutations in one request is common antipattern. For such cases better to create one complex mutation.

But even if you need to do such operation with several mutations you may use aliases:

await graphql({
  schema,
  source: `
  mutation {
    op1: article { like(id: 1) }
    op2: article { like(id: 2) }
    op3: article { unlike(id: 3) }
    op4: article { like(id: 4) }
  }
`,
});

expect(serialResults).toEqual([
  'like 1 executed with timeout 100ms',
  'like 2 executed with timeout 100ms',
  'unlike 3 executed with timeout 5ms',
  'like 4 executed with timeout 100ms',
]);

See the following test case: https://github.com/nodkz/conf-talks/blob/master/articles/graphql/schema-design/tests/mutations-test.js

Methods like/unlike are async with timeouts and works sequentially