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

你能解释一下为什么变异的输入参数为对象时应该是 输入类型吗?我认为更简单的方法是只重用 类型而不提供 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 + 属性的对象时,这将成为一种负担。

55483 次浏览

杰西的评论是正确的。为了得到更正式的回答,以下是 关于输入类型的 GraphQL 文档的节选:

上面定义的 Object 类型在这里不适合重用, 因为对象可以包含表示循环引用或 对接口和联合的引用,这两者都不合适 由于这个原因,输入对象有一个 系统中的独立类型。

更新

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

规格说明:

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

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

功能

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

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

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
}

所有这些示例都说明了一个重要的问题——尽管输入对象类型有时可能会反映对象类型,但由于业务需求的原因,在生产模式中不太可能看到这种情况。

仅仅是因为您不想将公共 API 与域模型或数据库模型耦合。传入数据在响应数据中的表示方式不同。您不希望对传入和传出的数据类型重用相同的数据类型... ... 或者您将完全将您的用户绑定到您的域或数据库模型,这意味着您所做的任何更改都可能破坏您的客户机

什么是输入类型,本质上就是请求类型。当您考虑交付机制(发出请求的客户机)和交付机制与控制器下的业务逻辑之间的边界时,例如,当您考虑六边形体系结构时。