具有动态键的对象的 Apollo/GraphQL 字段类型
Posted
技术标签:
【中文标题】具有动态键的对象的 Apollo/GraphQL 字段类型【英文标题】:Apollo/GraphQL field type for object with dynamic keys 【发布时间】:2020-09-16 20:55:43 【问题描述】:假设我的 graphql 服务器想要以 JSON 格式获取以下数据,其中 person3
和 person5
是一些 id:
"persons":
"person3":
"id": "person3",
"name": "Mike"
,
"person5":
"id": "person5",
"name": "Lisa"
问题:如何用 apollo 创建 schema 类型定义?
这里的键 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
的数组,但有没有办法像这样处理数据?
【问题讨论】:
你能澄清b
和g
是dynamically generated depending on my query
的意思吗?是否存在一个或另一个取决于请求中存在的字段?
@DanielRearden 所以b
和g
是id。也许我应该在问题中更清楚地说明这一点。该查询将包含仅获取一部分人的选项,因此对于一个查询,响应将包含 ID 为 a
、b
、c
的人员,对于另一个查询,例如 b
和 g
,如问题。
@DanielRearden 我现在将b
更改为person3
并将g
更改为person 5
,并添加了一些文本和一个变量以使其更清晰。该查询将包含仅获取文本中现在概述的一部分人的选项。
github.com/graphql/graphql-spec/issues/101
【参考方案1】:
GraphQL 依赖于服务器和客户端提前知道每种类型可用的字段。在某些情况下,客户端可以发现这些字段(通过自省),但对于服务器来说,它们总是需要提前知道。因此,实际上不可能以某种方式根据返回的数据动态生成这些字段。
您可以使用自定义 JSON scalar(graphql-type-json 模块)并将其返回给您的查询:
type Query
persons(area: String): JSON
通过使用 JSON,您可以绕过返回数据以适合任何特定结构的要求,因此您可以发送回您想要的任何内容,只要它是格式正确的 JSON。
当然,这样做有很大的缺点。例如,您失去了您以前使用的类型提供的安全网(实际上任何结构都可以返回,如果您返回错误的结构,您将不会发现它,直到客户尝试使用它并失败)。您也无法对返回数据中的任何字段使用解析器。
但是……你的葬礼:)
顺便说一句,在将数据发送回客户端之前,我会考虑将数据展平到一个数组中(就像您在问题中建议的那样)。如果您正在编写客户端代码,并使用动态大小的客户列表,那么使用数组可能比使用 id 键控的对象更容易使用。例如,如果您使用 React 并为每个客户显示一个组件,那么您最终会将该对象转换为数组以进行映射。在设计您的 API 时,与避免对您的数据进行额外处理相比,我会将客户端可用性作为更高的考虑因素。
【讨论】:
感谢您的 cmets!我转换来自服务器的传入数据。仅供参考:使用 id 的对象的原因是在客户端检索速度更快,因为它只是查找 id 以访问特定的人。 我的情况是通过 Rails / GraphQL API 返回验证错误。我事先不知道哪些键会出错,因此我使用的是自定义 JSON 标量。 实际上我改变了使用嵌套数组的方法,这使我能够始终如一地返回有错误的字段。【参考方案2】:您可以编写自己的GraphQLScalarType
并准确描述您的对象和动态键,允许和不允许或转换的内容。
见https://graphql.org/graphql-js/type/#graphqlscalartype
您可以查看taion/graphql-type-json,他在其中创建了一个允许和转换任何类型内容的标量:
https://github.com/taion/graphql-type-json/blob/master/src/index.js
【讨论】:
【参考方案3】:我在架构中的动态键上遇到了类似的问题,最终采用了这样的解决方案:
query lookupPersons
persons
personKeys
person3: personValue(key: "person3")
id
name
返回:
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 架构是什么样子的?我有点不清楚。 (您只显示查询的客户端提供的形状和服务器的响应)以上是关于具有动态键的对象的 Apollo/GraphQL 字段类型的主要内容,如果未能解决你的问题,请参考以下文章
有没有办法避免在 Apollo GraphQL 中返回具有空值的键?