Apollo/GraphQL 具有动态键的对象的字段类型
Apollo/GraphQL field type for object with dynamic keys
假设我的 graphql 服务器想要获取以下数据作为 JSON 其中 person3
和 person5
是一些 id:
"persons": {
"person3": {
"id": "person3",
"name": "Mike"
},
"person5": {
"id": "person5",
"name": "Lisa"
}
}
问题:如何使用 apollo 创建模式类型定义?
这里的键person3
和person5
是根据我的查询动态生成的(即查询中使用的area
)。所以在另一个时间我可能会返回 person1
、person2
、person3
。
如您所见,persons
不是 Iterable,因此以下内容不能用作我对 apollo 所做的 graphql 类型定义:
type Person {
id: String
name: String
}
type Query {
persons(area: String): [Person]
}
persons
对象中的键可能总是不同的。
当然,一个解决方案是将传入的 JSON 数据转换为使用 persons
的数组,但是没有办法处理这样的数据吗?
GraphQL 依赖于服务器和客户端都提前知道每种类型可用的字段。在某些情况下,客户端可以发现这些字段(通过内省),但对于服务器来说,它们总是需要提前知道。因此,以某种方式基于 returned 数据动态生成这些字段是不可能的。
您可以利用自定义JSON scalar(graphql-type-json模块)和return用于您的查询:
type Query {
persons(area: String): JSON
}
通过利用 JSON,您绕过了 returned 数据适合任何特定结构的要求,因此只要格式正确,您就可以发回任何您想要的数据 JSON .
当然,这样做有很大的缺点。例如,你失去了你以前使用的类型提供的安全网(实际上任何结构都可以 returned,如果你 returning 错误,你就赢了在客户端尝试使用它但失败之前找出它)。您还将失去对 returned 数据中的任何字段使用解析器的能力。
但是……你的葬礼:)
顺便说一句,在将数据发送回客户端之前,我会考虑将数据展平为一个数组(就像您在问题中建议的那样)。如果您正在编写客户端代码,并使用动态大小的客户列表,那么使用数组可能比使用 id 键控的对象更容易使用。例如,如果您正在使用 React,并为每个客户显示一个组件,您最终会将该对象转换为一个数组以无论如何对其进行映射。在设计您的 API 时,我会优先考虑客户端的可用性,而不是避免对您的数据进行额外处理。
您可以编写自己的 GraphQLScalarType
并准确描述您的对象和动态密钥、允许的内容和不允许的内容或转换。
见https://graphql.org/graphql-js/type/#graphqlscalartype
您可以查看 taion/graphql-type-json,他在其中创建了一个允许和转换任何类型内容的标量:
https://github.com/taion/graphql-type-json/blob/master/src/index.js
我在模式中遇到了与动态键类似的问题,最终采用了这样的解决方案:
query lookupPersons {
persons {
personKeys
person3: personValue(key: "person3") {
id
name
}
}
}
returns:
{
data: {
persons: {
personKeys: ["person1", "person2", "person3"]
person3: {
id: "person3"
name: "Mike"
}
}
}
}
通过将复杂性转移到查询中,它简化了响应形状。
与 JSON 方法相比的优点是它不需要来自客户端的任何反序列化
Venryx 的其他信息: 适合我的查询的可能架构如下所示:
type Person {
id: String
name: String
}
type PersonsResult {
personKeys: [String]
personValue(key: String): Person
}
type Query {
persons(area: String): PersonsResult
}
顺便说一句,如果您的人员数据集足够大,您可能还需要在 personKeys
上进行分页,此时,您应该查看 https://relay.dev/graphql/connections.htm
假设我的 graphql 服务器想要获取以下数据作为 JSON 其中 person3
和 person5
是一些 id:
"persons": {
"person3": {
"id": "person3",
"name": "Mike"
},
"person5": {
"id": "person5",
"name": "Lisa"
}
}
问题:如何使用 apollo 创建模式类型定义?
这里的键person3
和person5
是根据我的查询动态生成的(即查询中使用的area
)。所以在另一个时间我可能会返回 person1
、person2
、person3
。
如您所见,persons
不是 Iterable,因此以下内容不能用作我对 apollo 所做的 graphql 类型定义:
type Person {
id: String
name: String
}
type Query {
persons(area: String): [Person]
}
persons
对象中的键可能总是不同的。
当然,一个解决方案是将传入的 JSON 数据转换为使用 persons
的数组,但是没有办法处理这样的数据吗?
GraphQL 依赖于服务器和客户端都提前知道每种类型可用的字段。在某些情况下,客户端可以发现这些字段(通过内省),但对于服务器来说,它们总是需要提前知道。因此,以某种方式基于 returned 数据动态生成这些字段是不可能的。
您可以利用自定义JSON scalar(graphql-type-json模块)和return用于您的查询:
type Query {
persons(area: String): JSON
}
通过利用 JSON,您绕过了 returned 数据适合任何特定结构的要求,因此只要格式正确,您就可以发回任何您想要的数据 JSON .
当然,这样做有很大的缺点。例如,你失去了你以前使用的类型提供的安全网(实际上任何结构都可以 returned,如果你 returning 错误,你就赢了在客户端尝试使用它但失败之前找出它)。您还将失去对 returned 数据中的任何字段使用解析器的能力。
但是……你的葬礼:)
顺便说一句,在将数据发送回客户端之前,我会考虑将数据展平为一个数组(就像您在问题中建议的那样)。如果您正在编写客户端代码,并使用动态大小的客户列表,那么使用数组可能比使用 id 键控的对象更容易使用。例如,如果您正在使用 React,并为每个客户显示一个组件,您最终会将该对象转换为一个数组以无论如何对其进行映射。在设计您的 API 时,我会优先考虑客户端的可用性,而不是避免对您的数据进行额外处理。
您可以编写自己的 GraphQLScalarType
并准确描述您的对象和动态密钥、允许的内容和不允许的内容或转换。
见https://graphql.org/graphql-js/type/#graphqlscalartype
您可以查看 taion/graphql-type-json,他在其中创建了一个允许和转换任何类型内容的标量:
https://github.com/taion/graphql-type-json/blob/master/src/index.js
我在模式中遇到了与动态键类似的问题,最终采用了这样的解决方案:
query lookupPersons {
persons {
personKeys
person3: personValue(key: "person3") {
id
name
}
}
}
returns:
{
data: {
persons: {
personKeys: ["person1", "person2", "person3"]
person3: {
id: "person3"
name: "Mike"
}
}
}
}
通过将复杂性转移到查询中,它简化了响应形状。 与 JSON 方法相比的优点是它不需要来自客户端的任何反序列化
Venryx 的其他信息: 适合我的查询的可能架构如下所示:
type Person {
id: String
name: String
}
type PersonsResult {
personKeys: [String]
personValue(key: String): Person
}
type Query {
persons(area: String): PersonsResult
}
顺便说一句,如果您的人员数据集足够大,您可能还需要在 personKeys
上进行分页,此时,您应该查看 https://relay.dev/graphql/connections.htm