GraphQL简介
GraphQL主要是一套针对图状数据查询语言,推荐先读读知乎——什么是 GraphQL?
基本类型
先看看提供的基本类型(标量类型)
GraphQL 自带一组默认标量类型:
- Int:有符号 32 位整数。
- Float:有符号双精度浮点值。
- String:UTF‐8 字符序列。
- Boolean:true 或者 false。
- ID:ID 标量类型表示一个唯一标识符,通常用以重新获取对象或者作为缓存中的键。ID 类型使用和 String 一样的方式序列化;然而将其定义为 ID 意味着并不需要人类可读型。
自定义基本类型(Date是一个type,需要自行实现)
scalar Date
枚举定义
通过enum关键字定义枚举,如
enum Episode {
NEWHOPE
EMPIRE
JEDI
}
类定义
通过使用type进行类型定义,如
type Character {
str: String
}
GraphQL中,成员的表示方式是 成员名称:类型名称
memberName : typeName
通过在类型名称后加上!,代表该成员不能为空,比如如下定义了一个不能为空的数组成员
type Company{
human: [String]!
}
在数组的基类型右边加上!代表数组元素不能为空,如下是一个不能为空的数组,且数组元素不能为空
type Company{
human: [String!]!
}
接口类型
通过不使用type而使用interface关键字定义一个接口,type可以去实现接口,这要求其实现其所有成员,所以这里的接口更像一些规则的集合而非属性的抽象(需要具备接口的成员),实现多个接口使用&分隔,如
interface Company{
human: [String!]!
}
interface Home{
family: [String!]!
}
type YytCompany implements Company & Home{
human: [String!]!
host: String!
family: [String!]!
}
联合类型
使用union关键字创建一个可以指代多种类型的类型,比如如下代码表示创建了一个联合类型Author,其可代表Yyt也可代表Eetal,具体值由运行时接收的对象确定
type Yyt{
name:String
}
type Eetal{
host:String
}
union Author = Yyt|Eetal;
成员默认值、方法成员
通过在类型右侧使用 = 加上 value,代表为成员提供默认值。通过在成员名称右侧加上形参列表代表为一个方法成员,格式如成员格式,如,方法形参支持 基本类型、枚举和输入类型
type Yyt{
name:String = "yyt"
}
type Eetal{
host:String = "yangyitao.top"
}
union Author = Yyt|Eetal;
type AQuery{
whois(arg : Int):Author
}
输入类型
输入类型代表可以作为方法成员参数的复杂类型,如下定义一个输入类型,则可以在方法参数中使用该类型
type Review{
stars: Int!
commentary: String
}
input ReviewInput {
stars: Int!
commentary: String
}
type AQuery{
Review(arg : ReviewInput):Review
}
命名空间
通过schema关键字指定某些type为命名空间,如
type Query{
}
type Mutation{
}
schema {
query: Query
mutation: Mutation
}
查询
- 服务端定义
type Query{
}
type Mutation{
}
schema {
query: Query
mutation: Mutation
}
type Hello{
name: String = "hello"
world: Int = 1
whois(arg2:String) : String
}
type SubHello{
Hello(arg1:String):Hello
}
- 简单查询语句
查询语句直接使用对象格式的导航格式,同级别字段不需要逗号分隔
语句如果使用query作为schema,可以省略schema名称
因为查询结果使用json表示,默认使用查询的成员名称作为键,所以多重查询会造成重复覆盖
需要通过使用别名来避免,格式是
别名: 查询语句
如下代码包含一个不使用别名的查询和一个指定别名的查询
query {
Hello{
name
}
hello2:Hello{
name
world
}
}
- 查询结果
{
"data":{
"Hello":{
"name":"hello"
},
"hello2":{
"name":"hello",
"world":1
}
}
}
- 条件查询语句
如果有些方法成员,需要传递参数,可以通过如下调用
query {
SubHello{
Hello(arg1 : "Hi"){
whois(arg2 : "Hi")
}
}
}
查询结果
{
"data" : {
"SubHello":{
"Hello":{
"whois" : "这里展示的是solve函数具体实现结果"
}
}
}
}
可以看到,有时层级传递参数是一样的,可以通过在查询语句中定义变量传递,如刚刚的查询可以改为
query Sample($arg : String = "yyt"){
SubHello{
Hello(arg1: $arg){
whois(arg2 : $arg)
}
}
}
- 指令
GraphQL还提供@include和@skip,分别代表当表达式成立才执行对应查询,比如
query Sample($arg : String = "yyt" , $withName : Boolean = false,$skipWorld : Boolean = true){
SubHello{
Hello(arg1: $arg){
whois(arg2 : $arg)
name @inclue(if : $withName)
world @skip(if : $skipWorld)
}
}
}
执行结果
{
"data" : {
"SubHello":{
"Hello":{
"whois" : "这里展示的是solve函数具体实现结果"
}
}
}
}
- 片段
通过fragment关键字,可以定义查询片段,则在需要的地方通过
…片段名
即可完成引入片段代码
比如
fragment nameFrag{
name
}
query {
Hello{
...nameFrag
}
}
- on关键字
这里我也不知道叫什么好,官方的文档里没有提交这个的叫法,只说了他怎么用,因为查询具备多态性(可能是接口实现,也可能是聚合类型作为方法成员返回值类型),也就是有些方法返回的结果类型具备多个可能,则可以通过on关键字,指定返回不同类型时,筛选展示不同的字段,比如
{
search(text: "an") {
__typename
... on Human {
name
height
}
... on Droid {
name
primaryFunction
}
... on Starship {
name
length
}
}
}
这是官网的demo,阅读了官网的文档真的是有点糟糕,在表明场景前应该先把定义说明比较好吧?上述demo中意思是,__typename是返回当前结果类型,on Human代表如果返回类型是Human就使用前面的运算符运算后面的右值,否则此句查询无效(没有{name height}这个),没错这个是片段运算,这是一种简写,也叫内联片段,所以也可以使用on定义模板,如刚刚查询可以写为
fragment nameFrag on Hello{
name
}
query {
Hello{
...nameFrag
}
}
为什么没有写服务端的方法实现
因为GraphQL的服务端实现是多语言版本的,可以理解为,其核心是提供这种查询语句的规范服务端定义好这些图状数据,客户端自己定制查询语句。而这些方法成员一般为在服务端,不同语言下,传入一个resolve函数(也就是方法成员的实现,去完成服务端语言的脚本计算。)比如如下为js版本实现
//导入依赖
var { graphql, buildSchema } = require('graphql');
//定义schema
var schema = buildSchema(`
type Query {
hello: String
}
`);
//定义resolve,也就是图状数据,因为在服务端,可以传递服务端语言的函数作为成员值,取值时执行计算
var root = { hello: () => 'Hello world!' };
//查询hello
graphql(schema, '{ hello }', root).then((response) => {
console.log(response);
});
更多文章,请搜索公众号歪歪梯Club