GraphQL基础篇二
Posted PatWu16
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GraphQL基础篇二相关的知识,希望对你有一定的参考价值。
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/
以上是关于GraphQL基础篇二的主要内容,如果未能解决你的问题,请参考以下文章