GraphQL核心概念

GraphQL介绍

All of the data you need, in one request

GraphQL is an open spec for a flexible API layer.

Ask exactly what you want.

GraphQL是一个用于API的查询语言。GraphQL并没有和特定数据库或者存储引擎绑定,而是依靠现有的代码和数据支撑。和RESTful不同的是,GraphQL会在一个请求中获取所有想要的数据,比如我们想要从服务器获取id=1的书籍name信息和id=2的文章的title信息,则对于GraphQL请求,我们只需按照下方语法来发送请求即可获得想要的信息。

query{
  book(id:"1") {
    name
  },
  article(id:"2") {
  	title
  }
}

但是,对于RESTful API,我们就不得不按照类似于host:port/api/book/1/host:port/api/article/2/url来向服务器发送两次请求,然后对返回的数据进行筛选得到nametitle字段。这只是GraphQL和RESTful的其中一个区别,有关两者的比较,详见传送门

GraphQL相关文法

基于GraphQL的服务构建主要有四个部分:数据定义(schema)、查询(query)、更改(Mutation)、数据解析(Resolver)

数据定义

Schema

gqlgen is a schema-first library — before writing code, you describe your API using the GraphQL Schema Definition Language. This usually goes into a file called schema.graphql

首先,我们需要定义Schema(模型),在此文件中,我们需要定要定义各种数据类型。Schema 明确了服务端有哪些字段(用户自定义类型)可以用,每个字段的类型和子字段。每次查询时,服务器就会根据 Schema 验证并执行查询。

在Schema文件中,有4个比较特殊的关键字:

  • schema,标识这是一个GraphQL Schema定义,其中包含了用户可以进行的三种操作(可以省略)
  • query,定义查询操作,必须有
  • mutation,定义变更操作,可以省略
  • subscription,定义订阅操作,可以省略
schema {  
 query: Query
 mutation: Mutation
 subscription: Subscription
}

Type

Type关键字是用来定义抽象数据类型,类似于golang中的Type但是并不相同。在每一个自定义数据类型中,可以有多个Field(字段),每个Field可以再次指向某个Type

标量Scalar

Scalar是解析到单个标量对象的类型,无法再进行次级选择(次级选择的含义在阅读GraphQL查询语法之后会有所了解)。GraphQL中包含的标量有StringIntFloatBooleanEnumID

The ID scalar type represents a unique identifier, often used to refetch an object or as key for a cache.

# 定义性别标量
enum Gender {
    MALE
    FEMALE
}

对象Object

与我们在其他语言中定义对象类似:下方Person这个自定义数据类型中包括了idname等字段。

type People {
    name: String
    birth_year: String
    gender: String
}

上述People类型中只有标量字段,我们同样可以使用自定义数据类型字段,例如,我们定义了Film数据类型,每一部Film都有一个director字段:

type Film {
	name: string
	director: People
	...
}

接口Interface

接口是一个抽象类型,相信学习过go和java的读者都不陌生,下面直接看定义:

type Human {	# 实现Human的Type必须有这两个字段
	age: Int
    name: String
}
type Programmer implements Human {
	age: Int			
    name: String		
    Hair: Int
    field: String
}
type Student implements Human {
	age: Int
    name: String
    id:	ID
    major: String
}

列表和非空

对于上面People类型中的name字段,假如我们想要让其不为空,则可以在数据类型后面添加感叹号!,如果我们要新增字段参演电影的列表films,则可以使用[]

type People {
	name: String!
    birth_year: String
    gender: String
	films: [Film]
}

同样,我们可以对列表进行非空限制:

myField: [String!]

# 表示数组本身可以为空,但是其不能有任何控制成员
myField: null # 有效
myField: [] # 有效
myField: ['a', 'b'] # 有效
myField: ['a', null, 'b'] # 错误

myField: [String]!
# 这表示数组本身不能为空,但是其可以包含空值成员:
myField: null // 错误
myField: [] // 有效
myField: ['a', 'b'] // 有效
myField: ['a', null, 'b'] // 有效

联合类型

联合类型和接口十分相似,但是它并不指定类型之间的任何共同字段。

union SearchResult = Person | Film

任何返回一个 SearchResult 类型的地方,都可能得到一个 Person 或者 Film。注意,联合类型的成员需要是具体对象类型;不能使用接口或者其他联合类型来创造一个联合类型。

输入类型Input

输入常常用于变更(mutation)中,类似于post请求来新建对象。

input PersonInput {
	name: String
    birth_year: String
    gender: String
    films: [Film]
}

数据操作

查询(Query)

定义查询

在schema中,定义查询方法如下:

# 定义people查询方法,参数为必填字段id,返回数据类型为People
type Query {
    people(id: ID!): People
}

下面介绍一下如何查询:

参数查询

在下属查询方法中,people()为定义的查询方法

在这里插入图片描述

为查询起别名(Aliases)

如果我们先要再一次请求中查询两个people,则会出现下方的错误:

在这里插入图片描述

对于上述情况,我们可以使用别名的方式来进行查询:

在这里插入图片描述

使用片段(Fragment)

在上面的people5和people1的查询中,我们发现,两者都查询了name,此时只有一个字段还好,如果相同字段过多时,那应该怎么办呢?这种情况下我们便可以使用fragment:

在这里插入图片描述

片段的概念经常用于将复杂的应用数据需求分割成小块,特别是你要将大量不同片段的 UI 组件组合成一个初始数据获取的时候。

定义操作名称

上述所有查询,我们都使用了query关键字作为查询标识,虽然可以省略,但依然推荐这么加上。实际上,我们还可以为我们的查询定义名称,这对我们在开发过程中寻找可能存在的漏洞提供帮助。例如:定义一个名称为filmQuery的查询操作

query filmQuery{
  film(id:"1"){
    title
  }
}

后续将要讲述的mutation操作也可以定义名称。

使用变量(Variable)

使用变量的步骤:

  1. 使用 $variableName 替代查询中的静态值。
  2. 声明 $variableName 为查询接受的变量之一。
  3. variableName: value 通过传输专用(通常是 JSON)的分离的变量字典中。

在这里插入图片描述

使用变量可以很方便的在客户端构造查询语法,客户端可以构造一个复选框,下拉菜单等方式来获取动态参数,然后将动态参数提取到查询之外,作为分离的字典传进去。而不用构建一个全新的查询。(为了安全起见,我们不能使用用户提供的值来进行字符串插值构建查询)

我们也可以使用默认变量,定义如下:

query peopleQuery($id: ID = "5"){
    ...
}

变更(Mutation)

上述介绍的全部都是查询操作,GraphQL也为我们提供了mutation变更操作,用于修改数据。

定义变更
# 参数为Episode(Enum)以及一个Input类型的输入数据,返回类型为Review
type Mutation {
    createReview(episode: Episode!, review: ReviewInput!): Review
}
更新数据

变更和查询一样都可以使用变量以及片段等:

在这里插入图片描述

查询字段时,是并行执行,而变更字段时,是线性执行,一个接着一个。

订阅(Subscription)

订阅用于real-time实时请求。具体用法可以自行谷歌。

数据解析Resolver

当用户请求发送到服务器时,服务器如何进行相应并返回所需数据呢?下面介绍一下GraphQL的响应过程,以query查询为例:

在这里插入图片描述

  • 首先,GraphQL解析操作类型得知为query,查询方法为people
  • 之后,会尝试调用people解析(Resolver)函数,在此解析函数中,我们会调用其他函数(此函数通常需要自己手动实现)从数据库查询id35的people对象并返回,第一层解析结束。
  • 之后对第一层解析的返回值,进行第二层解析。当前查询字段为name,和films
    • title为String标量类型数据,则不必再深入解析
    • films为Film列表类型,调用Film解析(Resolver)函数。查询字段为title,由于是标量类型,则不必再深入解析。
    • ……
    • 最后将解析结果整合之后返回给客户端即可。

上述过程中大部分函数其实并不需要手动实现,这些操作对于我们来说相当于黑盒状态,我们接下来会介绍几个常用的GraphQL生成工具。

关于GraphQL的相关内容就介绍到这里,如果想有进一步的了解,可以前往GraphQL官网进一步学习。

GraphQL构建工具

以go语言为例,graphql构建工具有:

其中gqlgen支持语法最多,我的另一篇文章中介绍了如何使用gqlgen构建graphql服务

发布了79 篇原创文章 · 获赞 32 · 访问量 3万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览