如何 return graphql 解析器中的错误和数据?

How to return both error and data in a graphql resolver?

我正在考虑实现包含错误和数据的 graphql 响应的方法。

是否可以在不创建包含 error 的类型的情况下这样做?

例如

突变addMembersToTeam(membersIds: [ID!]! teamId: ID!): [Member] 将成员添加到某个团队。假设使用以下 membersIds 调用此突变:[1, 2, 3].

id 1 和2 的成员已经在团队中,因此必须抛出无法添加这些成员的错误,但是id 3 的成员不在团队中,因此应该添加。

我正在考虑使用 formatResponse,但似乎不会出现错误。

是否可以在不向 return 类型添加错误字段的情况下解决此问题?

如果您考虑合并 GraphQL 错误 - 在 Apollo 中有一种方法可以做到。 您需要将 errorPolicy 设置为 all。这将帮助您将错误通知用户,同时拥有尽可能多的数据。

none: This is the default policy to match how Apollo Client 1.0 worked. Any GraphQL Errors are treated the same as network errors and any data is ignored from the response.

ignore: Ignore allows you to read any data that is returned alongside GraphQL Errors, but doesn’t save the errors or report them to your UI.

all: Using the all policy is the best way to notify your users of potential issues while still showing as much data as possible from your server. It saves both data and errors into the Apollo Cache so your UI can use them.

但根据最佳实践,您不应该以这种方式操作它。 这是关于在 GraphQL 中处理错误的 great article

因此,更好的方法是添加 "errors" 字段作为响应的一部分并在 JS 代码中处理它。

Is it possible to solve this problem without adding error field to the return type?

很遗憾,没有。

解析器可以 return 数据,或 return null 并抛出错误。它不能两者兼顾。澄清一下,可能得到部分响应和一些错误。一个简单的例子:

const typeDefs = `
  type Query {
    foo: Foo
  }

  type Foo {
    a: String
    b: String
  }
`
const resolvers = {
  Query: {
    foo: () => {},
  }
  Foo: {
    a: () => 'A',
    b: () => new Error('Oops!'),
  }
}

在此示例中,查询 foo 上的两个字段将导致以下响应:

{
  "data": {
    "foo": {
      "a": "A",
      "b": null
    }
  },
  "errors": [
    {
      "message": "Oops",
      "locations": [
        {
          "line": 6,
          "column": 5
        }
      ],
      "path": [
        "foo",
        "b"
      ]
    }
  ]
}

这样一来,既可以传回数据,也可以传回错误。但是你不能像在你的问题中那样对同一个领域这样做。有几种方法可以解决这个问题。正如您所指出的,您可以 return 将错误作为响应的一部分,这通常是这样做的。然后,您可以使用 formatResponse,遍历结果数据,提取任何错误并将它们与任何其他 GraphQL 错误结合起来。不是最优的,但它可能会让你得到你正在寻找的行为。

另一种方法是修改 mutation,使其采用单个 memberId。然后,您可以为要添加的每个 ID 请求一个单独的突变:

add1: addMemberToTeam(memberId: $memberId1 teamId: $teamId): {
  id
}
add2: addMemberToTeam(memberId: $memberId2 teamId: $teamId): {
  id
}
add3: addMemberToTeam(memberId: $memberId3 teamId: $teamId): {
  id
}

这在处理客户端时可能比较棘手,当然效率较低,但同样可能会让您获得预期的行为。

我们可以通过联合来实现。我建议您访问这篇很棒的文章 Handling GraphQL errors like a champ

示例:

Mutation part: We can return the union type for the response & capture the result according to types.

type MemberType {
  id: ID!
  name: String!
}

enum ErrorType {
  BAD_REQUEST_ERROR
  FORBIDDEN_ERROR
  INTERNAL_SERVER_ERROR
  NOT_FOUND_ERROR
  UNAUTHORIZED_ERROR
}

type GraphqlError {
  type: ErrorType!
  code: String!
  message: String!
  helpLink: URL
}


union UserRegisterResult = MemberType | GraphqlError;
addMembersToTeam(membersIds: [ID!]! teamId: ID!): UserRegisterResult!

Response:

addMembersToTeam(membersIds: [ID!]! teamId: ID!): {
...on MemberType{
  id,
  name,
}
...on GraphqlError{
  id,
  message,
  statusCode,
}
}