GraphQL中输入类型的意义是什么?


83

您能解释一下为什么如果突变的输入参数是对象,那么它应该是输入类型吗?我认为更简单的只是重用类型而不提供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个以上属性的对象时,这将成为一种负担。


30
输入对象必须可序列化。由于输出对象可以包含循环,因此无法将其重复用于输入。
杰西·布坎南

1
杰西,看来答案足够了!您可以回答,我会这样标记。
德米特里·杜什金

想知道是否可以将接口与其结合起来
MVCDS '17

Answers:


99

从规格:

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
}

尽管这些结构可能会转换为适当的输入(我们创建了一个Student,所以我们也传递了一个表示其地址的对象),但通常它们却没有这样做-即,也许我们需要通过班级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
}

所有这些示例都说明了一个重点-尽管输入对象类型有时可能会映射对象类型,但是由于业务需求,您在生产模式中看到该对象的可能性要小得多。


4
这确实是一个很好的答案!
查理·伍

如果我错了,请纠正我,但在您的示例中,该类型Grade不能在input中重用StudentInput,对吗?您需要在输入对象中内联字段或具有GradeInput输入对象。
马特

2
@Matt好问题!在上面的示例中,Grade是一个枚举类型。不像对象,标量类型(如String,INT,等)和枚举类型可被用作两个输入类型输出类型。
丹尼尔·雷登

很好的解释
Visakh Vijayan

的确是个好答案!
约翰尼

25

杰西的评论是正确的。对于更正式的答案,这是GraphQL文档中有关输入类型的摘录:

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

更新

自发布以来,我发现循环引用实际上是可以接受的,只要它们为零即可(否则它将声明一个无限链)。但是,似乎还有其他限制(例如接口)似乎需要为输入使用单独的类型系统。


4
关于此限制的原因,这里还有更多讨论:github.com/graphql/graphql-js/issues/599
Cam Jackson
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.