Graphql 架构和类型
在此文章上面,您将了解有关GraphQL类型系统以及如何描述可查询数据的所有信息。由于GraphQL可以与任何后端框架或编程语言一起使用,因此我们将远离特定于实现的细节,并仅讨论这些概念。
参数
GraphQL对象类型上的每个字段都可以包含零个或多个参数,例如length
下面的字段:
type Starship {
id: ID!
name: String!
length(unit: LengthUnit = METER): Float
}
主要看length字段 括号里面有一个unit参数 LengthUnit 表示如果没有给参数 参数为空的话 METER 就是默认值
Float 是类型
LengthUnit 表示设置默认参数
查询和变异类型
模式中的大多数类型只是普通的对象类型,但在模式中有两种特殊类型:
schema {
query : Query
mutation : Mutation
}
每个GraphQL服务都有一个query
类型,可能有也可能没有mutation
类型。这些类型与常规对象类型相同,但它们是特殊的,因为它们定义了每个GraphQL查询的入口点。因此,如果您看到如下查询:
这意味着GraphQL服务需要一个Query
带有hero
和droid
字段的类型:
type Query {
hero (episode : Episode ): Character
droid (id : ID !): Droid
}
Mutation 跟以上Query 使用工作一样 ,区别就是 Query用于查询 Mutation 用于增删改 在什么场景使用那种类型即可
重要的是要记住,除了作为模式的“入口点”的特殊状态之外,Query
和Mutation
类型与任何其他GraphQL对象类型相同,并且它们的字段以完全相同的方式工作。
基础类型
GraphQL对象类型具有名称和字段,但在某些时候,这些字段必须解析为某些具体数据。这就是基础类型的来源:他们用来表示字段的类型的
在以下查询中,name
和appearsIn
字段将解析为基础类型:
我们知道这一点,因为这些字段没有任何子字段 - 它们是查询的叶子. 默认是Query
GraphQL带有一组开箱即用的默认标量类型:
-
Int
:带符号的32位整数。 -
Float
:带符号的双精度浮点值。 -
String
:UTF-8字符序列。 -
Boolean
:true
或false
。 -
ID
:ID标量类型表示唯一标识符,通常用于重新获取对象或作为缓存的键。ID类型以与String相同的方式序列化; 但是,将其定义为ID
表示它不是人类可读的。
在大多数GraphQL服务实现中,还有一种指定自定义标量类型的方法。例如,我们可以定义一个Date
类型:
scalar Date
nascimento: String @date
然后由我们的实现来定义应该如何序列化,反序列化和验证该类型。例如,您可以指定Date
应始终将类型序列化为整数时间戳,并且您的客户端应该知道期望任何日期字段的格式。
枚举类型
枚举类型也称为枚举,是一种特殊的标量,仅限于一组特定的允许值。这允许您:
- 验证此类型的任何参数是否为允许值之一
- 通过类型系统进行通信,字段将始终是一组有限的值
以下是GraphQL模式语言中枚举定义的含义:
enum Episode {
NEWHOPE
EMPIRE
JEDI
}
这意味着,无论我们使用的类型Episode
在我们的模式,我们希望它是正好一个NEWHOPE
,EMPIRE
或JEDI
。
请注意,各种语言的GraphQL服务实现将有自己的语言特定方式来处理枚举。在支持enum作为一等公民的语言中,实施可能会利用这一点; 在没有枚举支持的JavaScript语言中,这些值可能在内部映射到一组整数。但是,这些细节不会泄露给客户端,客户端可以完全根据枚举值的字符串名称进行操作。
列表和非空
对象类型,标量和枚举是您可以在GraphQL中定义的唯一类型。但是,当您在模式的其他部分或查询变量声明中使用类型时,可以应用影响这些值验证的其他类型修饰符。我们来看一个例子:
type Character {
name: String!
appearsIn: [Episode]!
}
在这里,我们使用一个String
类型,并通过在类型名称后添加感叹号将其标记为Non-Null!
。这意味着我们的服务器总是希望为该字段返回一个非空值,如果它最终得到一个实际触发GraphQL执行错误的空值,让客户端知道出现了问题。
在定义字段的参数时也可以使用Non-Null类型修饰符,如果将null值作为该参数传递,则会导致GraphQL服务器返回验证错误,无论是在GraphQL字符串中还是在变量中。
列表以类似的方式工作:我们可以使用类型修饰符将类型标记为a List
,这表示此字段将返回该类型的数组。在模式语言,这是通过缠绕在方括号中的类型来表示,[
和]
。它对于参数的作用相同,其中验证步骤将期望该值的数组。
可以组合Non-Null和List修饰符。例如,您可以拥有一个非空字符串列表:
myField : [ String !]
这意味着列表本身可以为null,但它不能包含任何null成员。例如,在JSON中:
myField : null //有效
myField : [ ] //有效
myField : [ 'a' , 'b' ] //有效
myField : [ 'a' , null , 'b' ] //错误
现在,假设我们定义了一个非空字符串列表:
myField : [ String ] !
这意味着列表本身不能为null,但它可以包含空值:
myField : null //错误
myField : [ ] //有效
myField : [ 'a' , 'b' ] //有效
myField : [ 'a' , null , 'b' ] //有效
您可以根据需要任意嵌套任意数量的Non-Null和List修饰符。
接口
与许多类型系统一样,GraphQL支持接口。一个接口是一个抽象类,其中包括一组特定的字段类型必须包括实现接口的。
例如,你可以有一个Character
代表星球大战三部曲中任何角色的界面:
interface Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
}
这意味着任何实现的 类型都Character
需要具有这些参数和返回类型的精确字段。
例如,以下是可能实现的一些类型Character
:
type Human implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
starships: [Starship]
totalCredits: Int
}
type Droid implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
primaryFunction: String
}
您可以看到这两种类型都具有Character
接口中的所有字段,但也会引入额外的字段totalCredits
,starships
以及primaryFunction
特定于该特定类型字符的字段。
当您想要返回一个对象或一组对象时,接口很有用,但这些对象可能有几种不同的类型。
例如,请注意以下查询会产生错误:
该hero
字段返回类型Character
,这意味着它可以是a Human
或Droid
取决于episode
参数。在上面的查询中,您只能询问Character
接口上存在的字段(不包括)primaryFunction
。
要询问特定对象类型的字段,您需要使用内联片段:
在查询指南的内联片段部分中了解有关此内容的更多信息。
大概就是 使用接口 可以用 ... on 接口名 { 字段 } 体现了什么类有什么字段吧
联盟类型
union SearchResult = Human | 机器人 | 星河
无论我们SearchResult
在模式中返回一个类型,我们都可以获得a Human
,Droid
或Starship
。请注意,union类型的成员需要是具体的对象类型; 您无法使用接口或其他联合创建联合类型。
在这种情况下,如果查询返回SearchResult
联合类型的字段,则需要使用条件片段才能查询任何字段:
该__typename
字段解析为String
允许您在客户端上区分不同数据类型的字段。
此外,在这种情况下,由于Human
和Droid
共享一个公共接口(Character
),您可以在一个地方查询它们的公共字段,而不必在多个类型中重复相同的字段:
{
search(text: "an") {
__typename
... on Character {
name
}
... on Human {
height
}
... on Droid {
primaryFunction
}
... on Starship {
name
length
}
}
}
请注意,name
仍然指定,Starship
因为否则它不会显示在给定的结果Starship
不是Character
!
输入类型
到目前为止,我们只讨论过将标量值(如枚举或字符串)作为参数传递到字段中。但您也可以轻松传递复杂对象。这在突变的情况下特别有价值,您可能希望传递要创建的整个对象。在GraphQL架构语言中,输入类型看起来与常规对象类型完全相同,但使用关键字input
而不是type
:
input ReviewInput {
stars: Int!
commentary: String
}
以下是如何在突变中使用输入对象类型:
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
{
"ep": "JEDI",
"review": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
{
"data": {
"createReview": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
}
输入对象类型上的字段本身可以引用输入对象类型,但不能在模式中混合输入和输出类型。输入对象类型也不能在其字段上包含参数。