具有动态键的对象的 Apollo/GraphQL 字段类型

Posted

技术标签:

【中文标题】具有动态键的对象的 Apollo/GraphQL 字段类型【英文标题】:Apollo/GraphQL field type for object with dynamic keys 【发布时间】:2020-09-16 20:55:43 【问题描述】:

假设我的 graphql 服务器想要以 JSON 格式获取以下数据,其中 person3person5 是一些 id:

"persons": 
  "person3": 
    "id": "person3",
    "name": "Mike"
  ,
  "person5": 
    "id": "person5",
    "name": "Lisa"
  

问题:如何用 apollo 创建 schema 类型定义?

这里的键 person3person5 是根据我的查询动态生成的(即查询中使用的 area)。所以在另一个时间我可能会得到person1person2person3 返回。 如您所见,persons 不是 Iterable,因此以下内容不能作为我对 apollo 所做的 graphql 类型定义:

type Person 
  id: String
  name: String

type Query 
  persons(area: String): [Person]

persons 对象中的键可能总是不同的。

当然,一种解决方案是将传入的 JSON 数据转换为使用 persons 的数组,但有没有办法像这样处理数据?

【问题讨论】:

你能澄清bgdynamically generated depending on my query的意思吗?是否存在一个或另一个取决于请求中存在的字段? @DanielRearden 所以bg 是id。也许我应该在问题中更清楚地说明这一点。该查询将包含仅获取一部分人的选项,因此对于一个查询,响应将包含 ID 为 abc 的人员,对于另一个查询,例如 bg,如问题。 @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 字段类型

有没有办法避免在 Apollo GraphQL 中返回具有空值的键?

Apollo / GraphQL 具有不同参数的相同查询

Apollo GraphQL 的动态标头(中间件之外)

如何使用 Apollo graphql 动态调整查询形状(字段)?

具有嵌套字段类型条件的 Apollo graphql 查询