【问题标题】:GraphQL 中输入类型的意义何在?
【发布时间】:2017-06-04 05:43:18
【问题描述】:

您能否解释一下为什么如果突变的输入参数是对象,它应该是输入类型?我认为简单得多,只需重用 type 而不提供 id。

例如:

type Sample {
  id: String
  name: String
}

input SampleInput {
  name: String
}

type RootMutation {
  addSample(sample: Sample): Sample  # <-- instead of it should be
  addSample(sample: SampleInput): Sample
}

对于小对象没关系,但是当架构中有大量具有 10 多个属性的对象时,这将成为一种负担。

【问题讨论】:

  • 输入对象必须是可序列化的。因为输出对象可以包含循环,所以它们不能重复用于输入。
  • 杰西,看起来已经足够回答了!你可以回答,我标记它。
  • 想知道是否可以将接口与其结合

标签: graphql


【解决方案1】:

来自规范:

GraphQL 对象类型 (ObjectTypeDefinition)... 不适合重用 [作为输入],因为对象类型可以包含定义参数的字段或包含对接口和联合的引用,这两者都不适合用作一个输入参数。因此,输入对象在系统中具有单独的类型。

这是“官方原因”,但有几个实际原因可以说明为什么不能将对象类型用作输入对象类型或将对象类型用作输入对象类型:

功能

对象类型和输入对象类型都具有字段,但是这些字段具有不同的属性,这些属性反映了架构如何使用这些类型。您的架构可能会为对象类型的字段定义参数和某种解析器函数,但这些属性在输入上下文中没有意义(即您不能解析输入对象的字段 -它已经有一个明确的值)。同样,只能为输入对象类型字段提供默认值,不能为对象类型字段提供默认值。

换句话说,这看起来像是重复:

type Student {
  name: String
  grade: Grade
}

input StudentInput {
  name: String
  grade: Grade
}

但是添加特定于对象类型或输入对象类型的功能可以清楚地表明它们的行为不同:

type Student {
  name(preferred: Boolean): String
  grade: Grade
}

input StudentInput {
  name: String
  grade: Grade = F
}

类型系统限制

GraphQL 中的类型分为输出类型输入类型

输出类型是可以作为 GraphQL 服务生成的响应的一部分返回的类型。输入类型是字段或指令参数的有效输入类型。

这两组之间存在重叠(即标量、枚举、列表和非空值)。但是,抽象类型如联合和接口在输入上下文中没有意义,不能用作输入。分离对象类型和输入对象类型可以确保在需要输入类型的地方永远不会使用抽象类型。

架构设计

在您的架构中表示实体时,某些实体可能确实会在其各自的输入和输出类型之间“共享字段”:

type Student {
  firstName: String
  lastName: String
  grade: Grade
}

input StudentInput {
  firstName: String
  lastName: String
  grade: Grade
}

但是,对象类型可以(实际上经常这样做)为非常复杂的数据结构建模:

type Student {
  fullName: String!
  classes: [Class!]!
  address: Address!
  emergencyContact: Contact
  # etc
}

虽然这些结构可能转换为适当的输入(我们创建了一个学生,所以我们也传递了一个代表他们地址的对象),但通常它们不会——也就是说,也许我们需要指定学生的类 ID 和部分 ID 的类,而不是对象。同样,我们可能有想要返回但不想改变的字段,反之亦然(例如 password 字段)。

此外,即使对于相对简单的实体,我们也经常对对象类型及其“对应”输入对象之间的可空性有不同的要求。通常我们希望保证一个字段也将在响应中返回,但我们不想在我们的输入中设置相同的字段。例如,

type Student {
  firstName: String!
  lastName: String!
}

input StudentInput {
  firstName: String
  lastName: String
}

最后,在许多模式中,给定实体的对象类型和输入对象类型之间通常没有一对一的映射。一种常见的模式是针对不同的操作使用单独的输入对象类型,以进一步微调模式级输入验证:

input CreateUserInput {
  firstName: String!
  lastName: String!
  email: String!
  password: String!
}

input UpdateUserInput {
  email: String
  password: String
}

所有这些示例都说明了一个重点——虽然输入对象类型有时可能反映对象类型,但由于业务需求,您不太可能在生产模式中看到这种情况。

【讨论】:

  • 这确实是一个很好的答案!
  • 如果我错了,请纠正我,但在您的示例中,Grade 类型不能在输入StudentInput 中重复使用,对吗?您需要在输入对象中内联字段或拥有GradeInput 输入对象。
  • @Matt 好问题!在上面的例子中,Grade 是一个枚举类型。与对象不同,标量类型(如 String、Int 等)和枚举类型可以用作both 输入类型 输出类型。
  • 很好的解释
  • @RoyLeibovitz 如上面“功能”部分所述,输出类型和输入类型的字段各自具有不同的属性,因此输出类型扩展输入类型(反之亦然)将是有问题的,并且规范不支持。根据您使用的语言、库和框架,可能可能使用一些 hacky 方法,但我强烈反对这样做。
【解决方案2】:

杰西的评论是正确的。更正式的答案,这里是GraphQL documentation on input types的摘录:

上面定义的 Object 类型不适合在这里重用, 因为对象可以包含表达循环引用的字段或 对接口和联合的引用,两者都不合适 用作输入参数。因此,输入对象具有 系统中的单独类型。

更新

自从发布以来,我发现循环引用实际上是可以接受的,只要它们是可空的(否则它会声明一个无限链)。但是,仍然存在其他限制(例如接口),似乎需要单独的输入类型系统。

【讨论】:

猜你喜欢
  • 2020-03-21
  • 2020-04-20
  • 2018-01-02
  • 2017-11-19
  • 2020-01-09
  • 2021-03-10
  • 2021-11-25
  • 2019-03-15
相关资源
最近更新 更多