通常,当您有一个可以解析为多种类型之一的字段时,您可以使用联合。如果这些类型共享一个或多个字段,您可能希望使用接口来代替。
您在模式中看到的一个常见模式是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!
}
在这里,aNode
可以是Foo
or 或 a 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 代替以达到相同的效果:
union SomeUnion = Foo | Bar
因此,为了减轻前端代码中的一些重复,您可以将每个Result
类型设为接口,或者更好的是,将单一Result
类型data
作为联合。不幸的是,接口或联合都不能与标量或列表一起使用,如果data
某些查询应该是标量或列表,这会使事情变得复杂。
但是,归根结底,首先可能不建议您以这种方式构建架构。避免这种结构有很多很好的理由:
- GraphQL 已经将您的查询结果作为带有
data
和errors
属性的 JSON 对象返回。
- 在 GraphQL 中返回错误
data
将需要额外的逻辑来捕获和格式化错误,而不是能够在任何地方抛出错误并让 GraphQL 为您处理错误报告。
- 您将无法捕获验证错误,因此您最终可能会在两个地方遇到错误——在
errors
数组内部和内部data.errors
。这也意味着您的客户需要在两个位置查找错误以进行正确的错误处理。
- GraphQL 专门设计用于允许部分解析响应。这意味着即使响应的某些部分出错并且无法解析,其他部分仍可能被解析并作为响应的一部分返回。这意味着响应“成功”的概念并不真正适用于 GraphQL。如果您绝对需要一个
success
字段,最好formatResponse
在查询解析后使用类似将其添加到响应对象中的方法。
坚持约定会让事情变得更加简单,并按照以下思路构建架构:
type Query {
login: LoginResponse
}
type LoginResponse {
token: String
user: User
}
实际响应仍将包括data
and errors
:
{
"data": {
"login": {
"token": "",
}
},
"errors": []
}
如果您甚至需要使用片段,您仍然需要每种类型一个片段,但片段之间的重复将大大减少。