Apollo 服务器使用部分数据引发多个错误
Posted
技术标签:
【中文标题】Apollo 服务器使用部分数据引发多个错误【英文标题】:Apollo server throw multiple errors with partial data 【发布时间】:2021-07-26 10:45:59 【问题描述】:我已经使用查询实现了 graphql apollo 服务器:
ordersSearch
name
orders:
id
name
对于不存在数据的订单,我想为带有如下数据的订单抛出未找到的错误数组:
"errors": [
"message": "404: Not Found",
"path": [ "ordersSearch", 0, "order", 1 ],
"extensions": …
,
"message": "404: Not Found",
"path": [ "ordersSearch", 0, "order", 3 ],
"extensions": …
],
"data": “ordersSearch”: [
"name": “test”,
"orders": [
“id": “2”,
name: “test”
]
在上述响应数据中未找到订单 #1 和 #3 并找到订单 #2。
如何在 apollo 服务器中实现这一点。目前,如果我抛出错误,查询执行将停止,并返回一个错误,data
属性为null
,如下所示:
"errors": [
"message": "404: Not Found",
"path": [ "ordersSearch", 0, "order", 1 ],
"extensions": …
],
"data": null
【问题讨论】:
也许我正在密切关注这个例子,而不是被问到的原理,但这对我来说很像 REST,而不是 GraphQL。你有什么理由不直接返回null
?那是 GraphQL 语言中的“未找到”。
我们想要一种方法让客户发现一些订单没有找到。此外,我们希望捕获并发送部分数据的其他模式道具中可能存在其他错误。
在这种情况下,null
是您告诉客户未找到某些订单的方式orders: [ ..., null, null, ...]
。
我们想让用户知道哪些订单 ID 没有找到。如果我们发送 null 则不可能
掩盖不良数据库设计似乎是人为问题...改为使用数据库一致性
【参考方案1】:
所以我想我明白你现在在寻找什么。听起来您拥有的对象存在,但您也在进行“一些查找”以查看它是否“完成”或“准备好”或类似的东西。这意味着您有“一些数据”可以返回,并且您想在有部分数据时告诉客户。
TLDR
只要您的解析器返回一个 Promise 数组,而不是单个 Promise,它就会按照您的要求工作。
问题:一个错误
对于这个例子,它只返回一个承诺:
const resolvers =
Query:
orders: async () =>
const orderIds = await checkForOrders();
// Wait for all promises to resolve, and then return the single value
const orders = await Promise.all(orderIds.map(async(orderId) =>
return await loadOrder(orderId); // Assume this can throw
));
return orders;
,
,
;
响应如下所示:
"errors": [
"message": "Order not found: 4adeaca9-81d5-482a-a846-2c09dabfde16",
"path": ["orders"]
],
"data":
"orders": null
解决方案:Promise 数组
现在,如果我更改我的解析器,使其返回一组承诺,而不仅仅是一个承诺:
const resolvers =
Query:
orders: async () =>
const orderIds = await checkForOrders();
// Return the array of promises
return orderIds.map(async(orderId) =>
return await loadOrder(orderId); // Assume this can throw
);
,
,
;
响应为您提供了您正在寻找的内容:
"errors": [
"message": "Order not found: 4adeaca9-81d5-482a-a846-2c09dabfde16",
"path": ["orders", 1]
,
"message": "Order not found: 8507aaf3-bfc2-46cb-bee2-358847cc632a",
"path": ["orders", 2]
],
"data":
"orders": [
"id": "f18e7627-9844-4927-8441-e61f660b72fd",
"name": "some name"
,
null,
null,
"id": "8af5089c-fa87-4c37-af0b-ce3b79aea6fc",
"name": "some other name"
]
编辑:
我意识到这不是你所要求的完全,因为我的回复中有一些 null
值在那里你只有成功,但你真的只能在一个对象的地方得到一个错误符合预期(并变为null
)。
如果你真的真的想要过滤掉那些 null
值,你可以将 formatResponse
中的一个函数传递给 ApolloServer 并将其删除。请注意,如果您这样做,path
值在语义上将不再是正确的。在我的示例中,我在数字 1 和 2 上抛出了错误,如果我要去掉 null
值,我的数组将只有 [0, 1] 和 [1] 中的值,所以path
表示我的条目 1 和 2 不好的字符串可能会导致智能 GraphQL 客户端出现问题。
【讨论】:
谢谢。这个解决方案就是我一直在寻找的***.com/a/41603497/4868839【参考方案2】:有两种广泛的解决方案可供考虑,但我们需要更多地了解您的 graphql 架构。
我假设您的架构如下:
type query
ordersSearch: OrdersSearch!
type OrdersSearch
name: String!
orders: [Order!]!
type Order
id: ID!
name: String!
1。对您的架构进行最少的更改
在您的架构中哪些字段被标记为不可为空,这在这里非常重要。
这是您的消息中让我假设您的架构有太多“!
”的部分。
如果我抛出错误,查询执行将停止,并返回一个错误,数据属性为 null
根据 GraphQL spec here:
如果发生错误的字段被声明为 Non-Null,则 null 结果将冒泡到下一个可为空的字段。在这种情况下,错误的路径应包括发生错误的结果字段的完整路径,即使响应中不存在该字段。
因此,如果您的 Order.name
字段解析器抛出错误,您的 GraphQL 服务器不能只返回空的 name
。它也不能返回 null Order
。如果您使用orders
数组的默认解析器,它不会跳过此空顺序,[Order!]!
数组将阻止空元素。所有这些都会冒泡到不能为空的ordersSearch
,你会得到data: null
。
那你能做什么?
您可以放宽非空约束。例如这个架构:
type OrdersSearch
name: String!
orders: [Order]! # Elements can be null.
这将允许以下形式的响应。不完全是您想要的,但足够接近。
"data": “ordersSearch”: [
"name": “test”,
"orders": [
null,
“id": “2”,
"name": “test”
,
null
]
2。使用 graphql 架构重新设计
如果预计会发生一些错误,并且后端和前端开发人员必须就其含义达成一致,那么不使用 GraphQL 错误是有意义的。只需将错误作为架构的一部分!然后你可以定义所有的行为。
这样的架构可以做到:
type query
ordersSearch: OrdersSearch!
type OrdersSearch
name: String!
orders: [OrderSearchResult!]!
type OrderSearchResult
found: Boolean!
order: Order
errorCode: OrderSearchResultErrorCode
type Order
id: ID!
name: String!
enum OrderSearchResultErrorCode
PRODUCT_OUT_OF_STOCK
PRODUCT_NOT_FOUND
【讨论】:
谢谢。这个解决方案就是我一直在寻找的***.com/a/41603497/4868839以上是关于Apollo 服务器使用部分数据引发多个错误的主要内容,如果未能解决你的问题,请参考以下文章