GraphQL 响应类型/片段之争
GraphQL response type / fragment struggle
我在用 graphql 编写 API 时遇到了一些困难。
我的 api 中的每个回复看起来应该都差不多。
所以理想情况下,这将是 graphql 类型:
type Response {
success
data {
... always different
}
errors {
path
message
}
}
但是因为这里的数据域总是不同的。每个 Mutation/Query 都应该有自己的响应类型(如果我正确理解 graphql)。
所以对于登录,这是我使用转换函数创建的类型:
type LoginResponse {
success
data {
user
token
}
errors {
path
message
}
}
现在在我的前端,我想使用以下片段,因为这些属性总是出现在每个响应中。
fragment Response on LoginResponse {
success
errors {
path
message
}
}
所以这里已经显示了我遇到的问题,您还使用一个片段定义了它的父类型。所以我必须创建与单独响应类型一样多的单独片段。
是否有人可能已经为此苦苦挣扎,或者是否有我没有看到的最佳实践
一般来说,当您有一个可以解析为多种类型之一的字段时,您可以使用联合。如果这些类型共享一个或多个字段,您可能希望改用接口。
您在模式中看到的一个常见模式是 Node
接口的概念。您可以查询通过 ID 获取节点,例如:
type Query {
node(id: ID!): Node
}
interface Node {
id: ID!
}
type Foo implements Node {
id: ID!
foo: String!
}
type Bar implements Node {
id: ID!
bar: Int!
}
在这里,一个Node
可以是Foo
或一个Bar
,所以如果我们要为Node
写一个片段,它可能看起来像这样:
fragment NodeFields on Node {
id # id is part of the interface itself
... on Bar {
bar # fields specific to Bar
}
... on Foo {
foo # fields specific to Foo
}
}
如果您没有共享字段,您可以使用联合来达到同样的效果:
union SomeUnion = Foo | Bar
因此,为了减少前端代码中的一些重复,您可以使每个 Result
类型成为一个接口,或者更好的是,让一个 Result
类型带有 data
是工会。不幸的是,接口或联合都不能与标量或列表一起使用,如果 data
对于某些查询应该是标量或列表,这会使事情变得复杂。
不过,归根结底,一开始就以这种方式构建架构可能并不明智。有很多理由可以避免这种结构:
- GraphQL 已经 returns 您的查询结果作为具有
data
和 errors
属性的 JSON 对象。
- 在 GraphQL 内部返回错误
data
将需要额外的逻辑来捕获和格式化错误,而不是能够在任何地方抛出错误并让 GraphQL 为您处理错误报告。
- 您将无法捕获验证错误,因此您最终可能会在两个地方出现错误——
errors
数组内部和 data.errors
内部。这也意味着您的客户端需要在两个位置查找错误以进行正确的错误处理。
- GraphQL 专门设计用于允许部分解析响应。这意味着即使响应的某些部分出错并且未能解决,其他部分仍可能得到解决并作为响应的一部分返回。这意味着响应 "successful" 的概念并不真正适用于 GraphQL。如果你绝对需要一个
success
字段,那么在查询解析后利用 formatResponse
之类的东西将它添加到响应对象会更好。
遵循惯例并按照以下方式构建您的架构将使事情变得更加简单:
type Query {
login: LoginResponse
}
type LoginResponse {
token: String
user: User
}
实际响应仍将包括 data
和 errors
:
{
"data": {
"login": {
"token": "",
}
},
"errors": []
}
如果您甚至需要使用片段,每种类型仍需要一个片段,但片段之间的重复会明显减少。
如何处理错误的好视频(与此问题相关):https://www.youtube.com/watch?v=-wRXk_QZ3Ko
我在用 graphql 编写 API 时遇到了一些困难。
我的 api 中的每个回复看起来应该都差不多。 所以理想情况下,这将是 graphql 类型:
type Response {
success
data {
... always different
}
errors {
path
message
}
}
但是因为这里的数据域总是不同的。每个 Mutation/Query 都应该有自己的响应类型(如果我正确理解 graphql)。
所以对于登录,这是我使用转换函数创建的类型:
type LoginResponse {
success
data {
user
token
}
errors {
path
message
}
}
现在在我的前端,我想使用以下片段,因为这些属性总是出现在每个响应中。
fragment Response on LoginResponse {
success
errors {
path
message
}
}
所以这里已经显示了我遇到的问题,您还使用一个片段定义了它的父类型。所以我必须创建与单独响应类型一样多的单独片段。
是否有人可能已经为此苦苦挣扎,或者是否有我没有看到的最佳实践
一般来说,当您有一个可以解析为多种类型之一的字段时,您可以使用联合。如果这些类型共享一个或多个字段,您可能希望改用接口。
您在模式中看到的一个常见模式是 Node
接口的概念。您可以查询通过 ID 获取节点,例如:
type Query {
node(id: ID!): Node
}
interface Node {
id: ID!
}
type Foo implements Node {
id: ID!
foo: String!
}
type Bar implements Node {
id: ID!
bar: Int!
}
在这里,一个Node
可以是Foo
或一个Bar
,所以如果我们要为Node
写一个片段,它可能看起来像这样:
fragment NodeFields on Node {
id # id is part of the interface itself
... on Bar {
bar # fields specific to Bar
}
... on Foo {
foo # fields specific to Foo
}
}
如果您没有共享字段,您可以使用联合来达到同样的效果:
union SomeUnion = Foo | Bar
因此,为了减少前端代码中的一些重复,您可以使每个 Result
类型成为一个接口,或者更好的是,让一个 Result
类型带有 data
是工会。不幸的是,接口或联合都不能与标量或列表一起使用,如果 data
对于某些查询应该是标量或列表,这会使事情变得复杂。
不过,归根结底,一开始就以这种方式构建架构可能并不明智。有很多理由可以避免这种结构:
- GraphQL 已经 returns 您的查询结果作为具有
data
和errors
属性的 JSON 对象。 - 在 GraphQL 内部返回错误
data
将需要额外的逻辑来捕获和格式化错误,而不是能够在任何地方抛出错误并让 GraphQL 为您处理错误报告。 - 您将无法捕获验证错误,因此您最终可能会在两个地方出现错误——
errors
数组内部和data.errors
内部。这也意味着您的客户端需要在两个位置查找错误以进行正确的错误处理。 - GraphQL 专门设计用于允许部分解析响应。这意味着即使响应的某些部分出错并且未能解决,其他部分仍可能得到解决并作为响应的一部分返回。这意味着响应 "successful" 的概念并不真正适用于 GraphQL。如果你绝对需要一个
success
字段,那么在查询解析后利用formatResponse
之类的东西将它添加到响应对象会更好。
遵循惯例并按照以下方式构建您的架构将使事情变得更加简单:
type Query {
login: LoginResponse
}
type LoginResponse {
token: String
user: User
}
实际响应仍将包括 data
和 errors
:
{
"data": {
"login": {
"token": "",
}
},
"errors": []
}
如果您甚至需要使用片段,每种类型仍需要一个片段,但片段之间的重复会明显减少。
如何处理错误的好视频(与此问题相关):https://www.youtube.com/watch?v=-wRXk_QZ3Ko