共计 4378 个字符,预计需要花费 11 分钟才能阅读完成。
GraphQL 之根底篇 – Schema 和类型
类型零碎(Type System)
因为一个 GraphQL 查问的构造和后果十分类似,因而即使不晓得服务器的状况,你也能预测查问会返回什么后果。然而一个对于咱们所须要的数据的确切形容仍然很有意义,咱们能抉择什么字段?服务器会返回哪种对象?这些对象下有哪些字段可用?这便是引入 schema 的起因。
每一个 GraphQL 服务都会定义一套类型,用以形容你可能从那个服务查问到的数据。每当查问到来,服务器就会依据 schema 验证并执行查问。
类型语言(Type Language)
GraphQL 服务能够用任何语言编写,它并不依赖于任何特定语言的句法句式(譬如 JavaScript)来与 GraphQL schema 沟通,咱们定义了本人的简略语言,称之为“GraphQL schema language”—— 它和 GraphQL 的查询语言很类似,让咱们可能和 GraphQL schema 之间能够无语言差别地沟通。
对象类型和字段(Object Types and Fields)
一个 GraphQL schema 中的最根本的组件是对象类型,它就示意你能够从服务上获取到什么类型的对象,以及这个对象有什么字段。应用 GraphQL schema language,咱们能够这样示意它:
type Character {
name: String!
appearsIn: [Episode!]!
}
- Character 是一个 GraphQL 对象类型,示意其是一个领有一些字段的类型。你的 schema 中的大多数类型都会是对象类型。
- name 和 appearsIn 是 Character 类型上的字段。这意味着在一个操作 Character 类型的 GraphQL 查问中的任何局部,都只能呈现 name 和 appearsIn 字段。
- String 是内置的标量类型之一 —— 标量类型是解析到单个标量对象的类型,无奈在查问中对它进行次级抉择。前面咱们将细述标量类型。
- String! 示意这个字段是非空的,GraphQL 服务保障当你查问这个字段后总会给你返回一个值。在类型语言外面,咱们用一个感叹号来示意这个个性。
[Episode!]!
示意一个 Episode 数组。因为它也是非空的,所以当你查问 appearsIn 字段的时候,你也总能失去一个数组(零个或者多个元素)。且因为 Episode! 也是非空的,你总是能够预期到数组中的每个我的项目都是一个 Episode 对象。
参数(Arguments)
GraphQL 对象类型上的每一个字段都可能有零个或者多个参数,例如上面的 length 字段:
type Starship {
id: ID!
name: String!
length(unit: LengthUnit = METER): Float
}
在 GraphQL 中,所有参数必须具名传递。本例中,length 字段定义了一个参数,unit。
参数可能是必选或者可选的,当一个参数是可选的,咱们能够定义一个默认值 —— 如果 unit 参数没有传递,那么它将会被默认设置为 METER。
查问和变更类型(The Query and Mutation Types)
你的 schema 中大部分的类型都是一般对象类型,然而一个 schema 内有两个非凡类型:
schema {query: Query mutation: Mutation}
标量类型(Scalar Types)
一个对象类型有本人的名字和字段,而某些时候,这些字段必然会解析到具体数据。这就是标量类型的起源:它们示意对应 GraphQL 查问的叶子节点。
下列查问中,name 和 appearsIn 字段将解析到标量类型:
{
hero {name appearsIn}
}
{
"data": {
"hero": {
"name": "R2-D2",
"appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"]
}
}
}
GraphQL 自带一组默认标量类型:
- Int:有符号 32 位整数。
- Float:有符号双精度浮点值。
- String:UTF‐8 字符序列。
- Boolean:true 或者 false。
- ID:ID 标量类型示意一个惟一标识符,通常用以从新获取对象或者作为缓存中的键。ID 类型应用和 String 一样的形式序列化;然而将其定义为 ID 意味着并不需要人类可读型。
大部分的 GraphQL 服务实现中,都有自定义标量类型的形式。例如,咱们能够定义一个 Date 类型:
scalar Date
枚举类型(Enumeration Types)
也称作枚举(enum),枚举类型是一种非凡的标量,它限度在一个非凡的可选值汇合内。这让你可能:
1. 验证这个类型的任何参数是可选值的的某一个
2. 与类型零碎沟通,一个字段总是一个无限值汇合的其中一个值。
上面是一个用 GraphQL schema 语言示意的 enum 定义:
enum Episode {NEWHOPE EMPIRE JEDI}
这示意无论咱们在 schema 的哪处应用了 Episode,都能够必定它返回的是 NEWHOPE、EMPIRE 和 JEDI 之一。
列表和非空(Lists and Non-Null)
对象类型、标量以及枚举是 GraphQL 中你惟一能够定义的类型品种。然而当你在 schema 的其余局部应用这些类型时,或者在你的查问变量申明处应用时,你能够给它们利用额定的类型修饰符来影响这些值的验证。咱们先来看一个例子:
type Character {
name: String!
appearsIn: [Episode]!
}
此处咱们应用了一个 String 类型,并通过在类型名前面增加一个感叹号! 将其标注为非空。这示意咱们的服务器对于这个字段,总是会返回一个非空值,如果它后果失去了一个空值,那么事实上将会触发一个 GraphQL 执行谬误,以让客户端晓得产生了谬误。
列表的运作形式也相似:咱们也能够应用一个类型修饰符来标记一个类型为 List,示意这个字段会返回这个类型的数组。在 GraphQL schema 语言中,咱们通过将类型包在方括号([和])中的形式来标记列表。列表对于参数也是一样的运作形式,验证的步骤会要求对应值为数组。
非空和列表修饰符能够组合应用。例如你能够要求一个非空字符串的数组:
myField: [String!]
不可为空的字符串数组:
myField: [String]!
接口(Interfaces)
跟许多类型零碎一样,GraphQL 反对接口。一个接口是一个形象类型,它蕴含某些字段,而对象类型必须蕴含这些字段,能力算实现了这个接口。
interface Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
}
例如,也有可能实现了 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
}
如果要查问一个只存在于特定对象类型上的字段,你须要应用内联片段:
query HeroForEpisode($ep: Episode!) {hero(episode: $ep) {
name
... on Droid {primaryFunction}
}
}
{
"data": {
"hero": {
"name": "R2-D2",
"primaryFunction": "Astromech"
}
}
}
联结类型(Union Types)
联结类型和接口十分相似,然而它并不指定类型之间的任何独特字段。
union SearchResult = Human | Droid | Starship
在咱们的 schema 中,任何返回一个 SearchResult 类型的中央,都可能失去一个 Human、Droid 或者 Starship。
这时候,如果你须要查问一个返回 SearchResult 联结类型的字段,那么你得应用条件片段能力查问任意字段。
{search(text: "an") {
__typename
... on Human {name height}
... on Droid {name primaryFunction}
... on Starship {name length}
}
}
{
"data": {
"search": [{
"__typename": "Human",
"name": "Han Solo",
"height": 1.8
}, {
"__typename": "Human",
"name": "Leia Organa",
"height": 1.5
}, {
"__typename": "Starship",
"name": "TIE Advanced x1",
"length": 9.2
}]
}
}
在这种状况下,因为 Human 和 Droid 共享一个公共接口(Character),你能够在一个中央查问它们的公共字段,而不用在多个类型中反复雷同的字段:
{search(text: "an") {
__typename
... on Character {name}
... on Human {height}
... on Droid {primaryFunction}
... on Starship {
name
length
}
}
}
输出类型(Input Types)
咱们只探讨过将例如枚举和字符串等标量值作为参数传递给字段,然而你也能很容易地传递简单对象。这在变更(mutation)中特地有用,因为有时候你须要传递一整个对象作为新建对象。
在 GraphQL schema language 中,输出对象看上去和惯例对象截然不同,除了关键字是 input 而不是 type:
input ReviewInput {stars: Int! commentary: String}
能够像这样在变更(mutation)中应用输出对象类型:
// 定义变量
{
"ep": "JEDI",
"review": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
// 发动变更
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {createReview(episode: $ep, review: $review) {
stars
commentary
}
}
// 响应
{
"data": {
"createReview": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
}
参考资料
https://graphql.cn/learn/schema/