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

Apollo/GraphQL field type for object with dynamic keys

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

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

问题:如何使用 apollo 创建模式类型定义?

这里的键person3person5是根据我的查询动态生成的(即查询中使用的area)。所以在另一个时间我可能会返回 person1person2person3。 如您所见,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