如果数组的一个元素无效,apollo 服务器 return 能否部分成功?
Can apollo server return a partial success if one element of an array is invalid?
假设我有一个查询 return 是一个客户对象数组,每个对象都有一个 id
、name
和 email
,所有这些都是不可为空。我的解析器从某个来源加载数据,可能是数据库,也可能是下游系统。返回的大部分数据都很好,但可能由于某种原因我们缺少单个客户的电子邮件地址。
目前,如果我的解析器只是 returns 所有已知客户的数组,包括狡猾的客户,那么整个操作将失败,如:
{
"data": {
"customers": null
},
"errors": [{
"message": "Cannot return null for non-nullable field Customer.email"
// More error stuff here
}]
}
我知道验证应该是严格的,但现在在我的 UI 上它无法加载整个客户列表,因为其中一个客户无效。一条记录很容易毒化整个数据库。
我想要的是一种仍然 return 错误的方法,但也让 data
字段填充所有其他有效的 Customer
记录。这可能与阿波罗服务器有关吗?或者我是否需要在尝试从我的解析器 return 之前手动验证所有数据?这将是相当艰巨的,因为我的真实数据结构要复杂得多。
从this discussion开始,似乎没有办法完全按照我的要求去做(在服务器端过滤掉无效记录)。
正确的做法是:
- 使
email
字段可以为空,因此服务器可以return每Customer
,其中一些可能有不完整的数据;或
- 使顶级
Customer
数组的元素可为空,这样服务器就可以 return 例如[customer1, null, customer3]
选项 1 意味着客户端可以访问有关错误的更多数据,因此即使电子邮件丢失,它仍然可以显示客户的其余详细信息。然而,它使数据结构不太可靠,因为现在任何使用电子邮件的地方都可能需要进行空检查。
选项 2 是一个更严重的失败,因为单个错误字段而使整个客户无效。但这让客户端更容易在顶层过滤掉不良客户记录,让前端代码的其余部分确信每个客户都有一个电子邮件地址。
在此基础上,我认为我更喜欢选项 2。
您可能对本文感兴趣:https://blog.logrocket.com/handling-graphql-errors-like-a-champ-with-unions-and-interfaces/
它讨论了使用联合类型来报告结果状态而不是传统的 GQL 错误报告。以下是您的情况下的示例:
type Customer {
id: ID!
name: String!
email: String!
}
interface CustomerErrorInterface {
message: String!
}
type MissingCustomerEmailError implements CustomerErrorInterface {
id: ID!
}
union CustomerResult = Customer | CustomerErrorInterface
type Query {
getCustomers: [CustomerResult!]!
}
然后,在客户端,您的查询将如下所示:
query getCustomers() {
__typename
... on Customer {
id
name
email
}
... on CustomerErrorInterface {
message
}
... on MissingCustomerEmailError {
id
}
}
这使您可以维护类型安全并避免使不应为空的内容为空,同时仍报告与您所拥有的数据关联的有意义的结果(成功和错误类型)。在联合中使用错误接口而不是具体的错误类型允许您以向后兼容的方式保留将来报告新类型错误的选项。
这与错误处理的“默认”GQL 方式(使用顶级 errors
输出)之间的区别在于,在界面中报告错误仍然允许提供有意义的参考数据(如 id
字段)在容易的位置而不需要解析错误信息,不同类型的错误可以酌情提供不同的参考数据。错误接口类型处理也更适合常规 GraphQL 输出类型模式(在我看来),而不是需要打开枚举来了解如何处理不同类型的错误。
允许输出列表中的 null 在这种特殊情况下可能会完成类似的事情,但是它不可能知道 哪个 数据有错误,特别是如果数据在问题不直接对应于可以通过传统错误处理提供的路径找到的用户提供的输入。
假设我有一个查询 return 是一个客户对象数组,每个对象都有一个 id
、name
和 email
,所有这些都是不可为空。我的解析器从某个来源加载数据,可能是数据库,也可能是下游系统。返回的大部分数据都很好,但可能由于某种原因我们缺少单个客户的电子邮件地址。
目前,如果我的解析器只是 returns 所有已知客户的数组,包括狡猾的客户,那么整个操作将失败,如:
{
"data": {
"customers": null
},
"errors": [{
"message": "Cannot return null for non-nullable field Customer.email"
// More error stuff here
}]
}
我知道验证应该是严格的,但现在在我的 UI 上它无法加载整个客户列表,因为其中一个客户无效。一条记录很容易毒化整个数据库。
我想要的是一种仍然 return 错误的方法,但也让 data
字段填充所有其他有效的 Customer
记录。这可能与阿波罗服务器有关吗?或者我是否需要在尝试从我的解析器 return 之前手动验证所有数据?这将是相当艰巨的,因为我的真实数据结构要复杂得多。
从this discussion开始,似乎没有办法完全按照我的要求去做(在服务器端过滤掉无效记录)。
正确的做法是:
- 使
email
字段可以为空,因此服务器可以return每Customer
,其中一些可能有不完整的数据;或 - 使顶级
Customer
数组的元素可为空,这样服务器就可以 return 例如[customer1, null, customer3]
选项 1 意味着客户端可以访问有关错误的更多数据,因此即使电子邮件丢失,它仍然可以显示客户的其余详细信息。然而,它使数据结构不太可靠,因为现在任何使用电子邮件的地方都可能需要进行空检查。
选项 2 是一个更严重的失败,因为单个错误字段而使整个客户无效。但这让客户端更容易在顶层过滤掉不良客户记录,让前端代码的其余部分确信每个客户都有一个电子邮件地址。
在此基础上,我认为我更喜欢选项 2。
您可能对本文感兴趣:https://blog.logrocket.com/handling-graphql-errors-like-a-champ-with-unions-and-interfaces/
它讨论了使用联合类型来报告结果状态而不是传统的 GQL 错误报告。以下是您的情况下的示例:
type Customer {
id: ID!
name: String!
email: String!
}
interface CustomerErrorInterface {
message: String!
}
type MissingCustomerEmailError implements CustomerErrorInterface {
id: ID!
}
union CustomerResult = Customer | CustomerErrorInterface
type Query {
getCustomers: [CustomerResult!]!
}
然后,在客户端,您的查询将如下所示:
query getCustomers() {
__typename
... on Customer {
id
name
email
}
... on CustomerErrorInterface {
message
}
... on MissingCustomerEmailError {
id
}
}
这使您可以维护类型安全并避免使不应为空的内容为空,同时仍报告与您所拥有的数据关联的有意义的结果(成功和错误类型)。在联合中使用错误接口而不是具体的错误类型允许您以向后兼容的方式保留将来报告新类型错误的选项。
这与错误处理的“默认”GQL 方式(使用顶级 errors
输出)之间的区别在于,在界面中报告错误仍然允许提供有意义的参考数据(如 id
字段)在容易的位置而不需要解析错误信息,不同类型的错误可以酌情提供不同的参考数据。错误接口类型处理也更适合常规 GraphQL 输出类型模式(在我看来),而不是需要打开枚举来了解如何处理不同类型的错误。
允许输出列表中的 null 在这种特殊情况下可能会完成类似的事情,但是它不可能知道 哪个 数据有错误,特别是如果数据在问题不直接对应于可以通过传统错误处理提供的路径找到的用户提供的输入。