Error handling 在graphql中执行多个突变时的正确错误处理

Error handling 在graphql中执行多个突变时的正确错误处理,error-handling,graphql,mutation,Error Handling,Graphql,Mutation,鉴于以下GraphQL突变: type Mutation { updateUser(id: ID!, newEmail: String!): User updatePost(id: ID!, newTitle: String!): Post } mutation($userId: ID!, $newEmail: String!) { updateUser(id: $userId, newEmail: $newEmail) { __typename ... on Us

鉴于以下GraphQL突变:

type Mutation {
  updateUser(id: ID!, newEmail: String!): User
  updatePost(id: ID!, newTitle: String!): Post
}
mutation($userId: ID!, $newEmail: String!) {
  updateUser(id: $userId, newEmail: $newEmail) {
    __typename
    ... on User {
      id
      email
    }
    ... on Error {
      message
      code
    }
  }
}
Apollo文档声明,完全有可能在一个请求中执行多个突变,比如

mutation($userId: ID!, $newEmail: String!, $postId: ID!, $newTitle: String!) {
  updateUser(id: $userId, newEmail: $newEmail) {
    id
    email
  }
  updatePost(id: $postId, newTitle: $newTitle) {
    id
    title
  }
}
1。有人真的这样做吗?如果你不明确地这样做,批处理会导致这种变异吗

2。如果您在on-mutation中执行运行多项操作,您将如何正确处理错误?

我见过很多人建议在服务器上抛出错误,这样服务器就会做出如下响应:

{
  errors: [
    {
      statusCode: 422,
      error: 'Unprocessable Entity'
      path: [
        'updateUser'
      ],
      message: {
        message: 'Validation failed',
        fields: {
          newEmail: 'The new email is not a valid email address.'
        }
      },
    },
    {
      statusCode: 422,
      error: 'Unprocessable Entity'
      path: [
        'updatePost'
      ],
      message: {
        message: 'Validation failed',
        fields: {
          newTitle: 'The given title is too short.'
        }
      },
    }
  ],
  data: {
    updateUser: null,
    updatePost: null,
  }
}
但我如何知道哪个错误属于哪个突变?我们不能假设
errors
数组中的第一个错误属于第一个突变,因为如果
updateUser
成功,数组将简单地包含一个条目。然后我是否必须迭代所有错误,并检查路径是否与我的突变名称匹配D

另一种方法是将错误包含在专用响应类型中,例如
UpdateUserResponse
UpdatePostResponse
。这种方法使我能够正确地解决错误

type UpdateUserResponse {
  error: Error
  user: User
}

type UpdatePostResponse {
  error: Error
  post: Post
}

但我觉得这会大大增加我的模式。

简言之,是的,如果包含多个顶级突变字段,请利用错误上的
path
属性来确定哪个突变失败。请注意,如果在图形的更深处(在某个子字段而不是根级别字段上)发生错误,则路径将反映该字段。也就是说,解析
title
字段时发生的执行错误将导致路径
updatePost.title

作为
数据的一部分返回错误
也是一个同样有效的选项。这种方法还有其他好处:

  • 像这样发送的错误可能包括额外的元数据(“代码”属性、有关可能产生错误的特定输入字段的信息等)。虽然可以通过
    errors
    数组发送相同的信息,但将其作为模式的一部分意味着客户端将知道这些错误对象的结构。这对于使用类型化语言编写的客户机尤其重要,因为客户机代码通常是从模式生成的
  • 通过这种方式返回客户机错误,您可以清楚地区分应该对用户可见的用户错误(错误的凭据、用户已经存在等)和客户机或服务器代码中实际出现的错误(在这种情况下,我们最多显示一些通用消息)
  • 这样创建一个“有效负载”对象可以让您在将来附加额外的字段,而不会破坏您的模式
第三种选择是以类似的方式使用工会:

type Mutation {
  updateUser(id: ID!, newEmail: String!): UpdateUserPayload!
}

union UpdateUserPayload = User | Error
这使客户端能够使用片段和
\uuu typename
字段来区分成功和失败的突变:

type Mutation {
  updateUser(id: ID!, newEmail: String!): User
  updatePost(id: ID!, newTitle: String!): Post
}
mutation($userId: ID!, $newEmail: String!) {
  updateUser(id: $userId, newEmail: $newEmail) {
    __typename
    ... on User {
      id
      email
    }
    ... on Error {
      message
      code
    }
  }
}
您甚至可以为每种错误创建特定类型,从而可以省略任何类型的“代码”字段:

这里没有对错的答案。虽然每种方法都有优点,但您选择哪种方法最终取决于偏好