GraphQL 第二章 Schema 和类型
定义了 schema 后我们就能知道服务器会返回那种对象,这些对象有哪些字段可用等。
每个 GraphQL 服务都会定义一套类型,用以描述你可能从那个服务查询到的数据,每当查询到来,服务器就会根据 schema 验证并执行查询。
之前我们讲了 GraphQL 的查询语言,这里我们来讲一下 GraphQL Schema language 。这个和 GraphQL 的查询语言类似。
一个 GraphQL schema 中的最基本的组件是对象类型,它表示你可以从服务上获取到什么样类型的对象,以及对象的字段。
type Human {
name: String!
height: Float
friends: [Person!]!
}
这里我们定义了一个 Human 对象类型,这个对象中包括了几个字段。 String
Float
是内置的标量类型,String!
表示这个字段是非空的,[person!]!
表示一个 person 数组,他也是非空的,所以当你查询 friends 时能得到一个数组,并且由于 Person!也是非空的,所以你可以得到的数组中每个项目都是一个 Person 对象.
GraphQL 对象类型上的每个字段都有可能有 0 或 多个参数
这里所有的参数都是具名的
type Human {
name: String!
height:(unit: HeightUnit = METER) Float
friends:[Person!]!
}
Schema 中有两个特殊的类型 Query 和 Mutation 。
schema {
query: Query
mutation: Mutation
}
每个 GraphQL 服务都有一个 query 类型,可能有一个 mutation 类型。这两个类型和常规对象类型无差,但是它们之所以特殊,是因为它们定义了每一个 GraphQL 查询的入口,这也是它们唯一特殊的地方,别的与普通对象类型无二致。
query {
hero { name }
person(id: 1) { name}
}
# -------->
type Query {
hero: Charactor
person(id: ID!): Charactor
}
标量类型(scalar type)
在之前我们就用到过好多次String
Float
这些类型了,这些类型叫做标量类型,这些类型是没有次级字段的,对象类型是有次级字段的。
GraphQL 自带一组默认标量类型:
* Int:有符号 32 位整数。
* Float:有符号双精度浮点值。
* String:UTF‐8 字符序列。
* Boolean:true 或者 false。
* ID:ID 标量类型表示一个唯一标识符,通常用以重新获取对象或者作为缓存中的键。ID 类型使用和 String 一样的方式序列化;然而将其定义为 ID 意味着并不需要人类可读型。
我们还可以自定义标量类型,取决于我们的实现中如何定义将其序列化、反序列化和验证。
scalar Date
# 例如定义一个Date 类型应该总是被序列化成整形时间戳,而客户端应该知道去要求任何 date 字段都是这样的格式
枚举类型(enumeration type)
这个其实和别的语言中的枚举类型是差不多的。枚举类型是一种特殊的标量,它限制在一个特殊的可选值集合内。
1. 验证这个类型的任何参数是可选值的某一个
2. 与类型系统沟通,一个字段总是一个有限值集合的其中一个值
enum Hobby {
swim
football
basketball
}
类型修饰符
我们之前就已经看到过两个了,分别是 !
[]
一个是用来标注 非空 ,一个是标注 数组
标记了非空的字段,服务器总会返回一个非空值,如果它得到一个空值,就会触发一个 GraphQL 执行错误。如果定义的参数有这个非空标识,传参时给个空值,会导致服务器返回一个验证错误。
列表就相对来说直观些了,通过[]
定义的就是一个数组了
接下来我们来看看这两者结合使用的情况
myField: [String!]
# ------------- #
myField: null # 有效
myField: [] # 有效
myField: ['a','b'] # 有效
myField: ['a',null,'b'] # 错误
myField: [String]!
# ------------ #
myField: null # 错误
muField: [] # 有效
myField: ['a','b'] # 有效
myField: ['a',null,'b'] # 有效
结口(Interface)
GraphQL 支持接口,一个接口是一个抽象类型,它包含某些字段,而实现这个接口的对象类型必须包含这些字段
interface Human {
id: ID!
name: String
age: Int
height: Float
friends:[Person!]!
}
实现 Human
接口
type Female implements Human {
id: ID!
name: String
age: Int
height: Float
friends:[Person!]!
feature: [String!]
}
type Male implements Human {
id: ID!
name: String
age: Int
height: Float
friends: [Person!]!
hobby: [String!]
}
联合类型(Union type)
联合类型和接口十分相似,但是它并不指定类型之间的任何共同字段,联合类型的成员需要是具体对象类型,你不能使用接口或者其他联合类型来创建一个联合类型。
union searchResult = Human | Hero | Person
输入类型(input type)
我们之前传递的参数都是一些标量类型或者是枚举类型的值,但是我们不可避免的会传递一些复杂对象,比如在新增、更新操作中,这时候就引入了输入对象类型,它和常规对象一样,只是关键字是 input
而不是 type
input person {
name: String
age: Int
...
}
mutation createPerson($person: Person){
createOne(one: $person){
name
age
}
}
# ---------- #
# person
{
name:'zs',
age: 10
}
输入对象类型上的字段本身也可以指代输入对象类型,但是你不能在你的 schema 混淆输入和输出类型。输入对象类型的字段当然也不能拥有参数。