如何在graphql解析器中同时返回错误和数据?

时间:2018-10-12 10:59:13

标签: node.js apollo graphql-js

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

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

例如

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

具有ID 1和2的成员已经在团队中,因此必须抛出一个错误,即不能添加这些成员,但是应该添加ID 3的成员,因为他不在团队中。

我当时正在考虑使用formatResponse,但似乎那里没错。

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

3 个答案:

答案 0 :(得分:1)

  

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

不幸的是,没有。

解析器可以返回数据,也可以返回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"
      ]
    }
  ]
}

通过这种方式,可以同时发送数据和错误。但是您不能在同一字段中这样做,就像您的问题一样。有两种解决方法。如您所指出的,您可以将错误作为响应的一部分返回,通常是这样做的。然后,您可以使用formatResponse,遍历结果数据,提取任何错误,并将其与其他GraphQL错误合并。不是最佳选择,但是它可以使您获得所需的行为。

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

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

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

答案 1 :(得分:0)

如果您考虑合并GraphQL错误-在Apollo中有一种解决方法。 您需要使用errorPolicyLink for documentation

将策略设置为all可以帮助您通知用户该错误,并同时拥有尽可能多的数据。

  

:这是与Apollo Client 1.0匹配的默认策略   工作了。任何GraphQL错误都与网络错误相同,并且   从响应中忽略任何数据。

     

忽略:忽略可让您   读取与GraphQL错误一起返回的所有数据,但不读取   保存错误或将错误报告给您的用户界面。

     

全部:使用全部策略   是在仍然发生时通知用户潜在问题的最佳方法   显示来自服务器的尽可能多的数据。它保存两个数据   并将错误导入Apollo缓存,以便您的UI可以使用它们。

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

因此,更可取的方法是添加"errors"字段作为响应的一部分,并以js代码进行处理。

答案 2 :(得分:0)

我们可以通过使用联合来实现。我建议您浏览精彩的文章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!
}

type ErrorType {
  id: ID!
  message: String!
  statusCode: Int!
}

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

Response:

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