GraphQL入门

GraphQL简介

GraphQL 是一种用于 API 的查询语言。它使得客户端能够准确地获得它需要的数据,而且没有任何冗余

特点

不多不少的请求你需要的数据

发出一个 GraphQL 请求就能准确获得你想要的数据,不多不少。 GraphQL 查询总是返回可预测的结果。使用 GraphQL 的应用可以工作得又快又稳,因为控制数据的是应用,而不是服务器。

获取多个资源只需要用一个请求

GraphQL 查询不仅能够获得资源的属性,还能沿着资源间引用进一步查询。典型的 REST API 请求多个资源时得载入多个 URL,而 GraphQL 可以通过一次请求就获取你应用所需的所有数据。这样一来,即使是比较慢的移动网络连接下,使用 GraphQL 的应用也能表现得足够迅速。

完善的类型系统

GraphQL API 是基于类型和字段的方式进行组织的。GraphQL 使用强类型来保证请求数据的正确性,提前声明好类型可以避免编写代码手动进行解析。它还提供了清晰的辅助性错误信息。

API 演进无需划分版本

给你的 GraphQL API 添加字段和类型而无需影响现有查询。老旧的字段可以废弃,从工具中隐藏。通过使用单一演进版本,GraphQL API 使得应用始终能够使用新的特性,并鼓励使用更加简洁、更好维护的服务端代码。

使用你现有的数据和代码

GraphQL 让你的整个应用共享一套 API,而不用被限制于特定存储引擎。GraphQL 引擎已经有多种语言实现,通过 GraphQL API 能够更好利用你的现有数据和代码。你只需要为类型系统的字段编写函数,GraphQL 就能通过优化并发的方式来调用它们

一个GraphQL查询可以包含一个或者多个操作(operation),类似于一个RESTful API。操作有两种类型:查询(Query)或者修改(mutation)

查询

第一个demo

1
2
3
4
5
6
query {
user(id: 1) {
id
name
}
}

user是查询的operation
(id: 1)包含了传入给Query的参数
查询包含id和name字段,这些字段也就是我们希望查询可以返回的数据

这时server返回的数据就类似下面这种:

1
2
3
4
5
6
7
8
{
"data": {
"user": {
"id": "1",
"name": "悬笔e绝"
}
}
}

第二个demo

1
2
3
4
5
6
7
8
query {
student(class_id: 1, order: "number DESC") {
name
class_id
number
hometown
}
}

这次我们查询student,并传入两个参数:class_id班号用于过滤,order指明按照number学号进行降序排列。查询中包含的字段是:name、class_id、number和hometown)

应该能猜到返回的结果是什么样的了,没错,大概如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"data": {
"student": [
{
"name": "悬笔e绝",
" class_id ": 1,
" number ": 16,
" hometown ": jiujiang
},
{
"name": "li",
" class_id ":1,
" number ": 15,
" hometown ":Shenzhen
}
]
}
}

从上面两个例子里可以看出来GraphQL允许客户端明确指定它要的是什么,避免了数据后去的冗余或者不足。而RESTful API ,每一个客户端都会对应很多个RESTful API或者一个API要服务很多个客户端。
所以说GraphQL是很好的查询语言。所有的operation、参数和所有可以查询的字段都需要在GraphQL server上定义、实现。

嵌套查询

查询的嵌套没有限制,全看我们的查询和server的实现。比如可以使用下面的查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
purchases(client_id: 1) {
date
quantity
total
product {
name
price
product_category {
name
}
}
client {
name
dob
}
}
}

这里我们请求server返回某个客户的purchases。查询里不仅指定了purchase的字段,还指定了相关的product,product_category的名称。

强类型

官方文档有一句话如下:

“每一个GraphQL server都要定义类型系统。查询是在这个类型系统的上下文中执行的。”


也就是说,你可以查询值类型:Int, Float, String, Boolean和ID
如果想要查询object类型,就需要我们自己定义了。

由于GraphQL查询都是结构化的,信息也是类树结构展示的。我们可以这么简单的理解:值类型(Scalar Type)–叶子,对象类型(Object Type)–树干。

字段别名

在GraphQL查询中可以为Operation里的字段指定别名。
比如查询里指定了字段hometown,但是客户端只能接受jiaxiang。
因为查询的返回结果都包含在以operation名称为key的对象里,所以可以为这个名称设置一个别名:

1
2
3
4
5
6
7
{
MrLi_student: student (class_id: 1) {
class_id
name
jiaxiang: hometown
}
}

返回的数据类似这样:

1
2
3
4
5
6
7
8
9
{
"data": {
" MrLi_student ": {
" class_id ": "1",
"name": "悬笔e绝 ",
" jiaxiang ": "jiujiang"
}
}
}

其他查询的功能

Fragments

一次查询的两个对象中包含了大量相同片段的时候,可以把重复的字段都提出来,放到一个fragment里;
用操作符“…” 就可以使用fragment了

变量

查询参数多处使用的时候,我们可以定义一个变量来方便使用

修改Mutation

GraphQL不仅可以用来查询数据,也可以创建、更新和销毁数据。
当然和查询一样,这些也需要server端有对应的实现。
增、删、改一类的operation在GraphQL里统称为Muration(修改)。
下面通过几个例子来演示一下mutation。

1
2
3
4
5
6
7
8
9
10
mutation {
create_student (
name: "张三"
class_id: 3
) {
name
class_id
hometown
}
}

我们现在指定operation的类型为mutation,而不是query。
在create_student操作里我们传入了创建一个student需要的数据,并最终返回一个查询集合:

1
2
3
4
5
6
7
8
9
{
"data": {
"create_student": {
"name": "张三",
"class_id": 3,
"hometown": "shenzhen"
}
}
}

修改和删除类似了,对应的update_student和destroy_student即可

定义文档

上个例子的createstudent、update student和destroy_ student这些operation都是在GraphQL server实现好的。那我们前台客户端怎么知道GraphQL server都实现了什么方法呢?于是就有了 定义文档
GraphQL会根据已经定义好的类型系统来自动生成出一个说明文档。这样你就不用一次一次的翻看代码,而直接查看文档来了解operation的全部实现细节。
如果你用的是express-graphql, 并设置graphiql为true的话,那么就会生成一个web的调试界面。在最右侧可以直接使用doc

查询所有的queryType类型的operation

1
2
3
4
5
6
7
8
9
10
{
__schema {
queryType {
name
fields {
name
}
}
}
}

对于mutation类型的操作也是类似的:

1
2
3
4
5
6
7
8
9
10
{
__schema {
mutationType {
name
fields {
name
}
}
}
}

返回结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"data": {
"__schema": {
"mutationType": {
"name": "Mutation",
"fields": [
{
"name": "create_student"
},
{
"name": "destroy_ student "
},
{
"name": "update_ student "
}
]
}
}
}
}

我们还可以具体查询operation的参数,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
__schema {
mutationType {
name
fields {
name
args {
name
}
}
}
}
}

结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"data": {
"__schema": {
" mutationType ": {
"name": " Mutation ",
"fields": [
{
"name": " create_student ",
"args": [
{
"name": "name"
},
{
"name": "class_id"
}
]
},
{
...
}
]
}
}
}
}

你会看到server实现了一个create_student的修改operation,参数为name和class_id。

搭建一个简单的基于node的GraphQL Server

第一步初始化项目

创建工程目录 graphqlDemo
初始化项目,以及引入所需的包

1
2
3
4
npm init -f
npm install graphql express express-graphql --save
npm install babel-cli babel-preset-es2015 --save-dev
npm install -g nodemon

第二步 mock数据

在项目根目录下创建 data 目录,并在该目录下创建 data.json 文件,并输入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"1": {
"id": "1",
"name": "悬笔e绝"
},
"2": {
"id": "2",
"name": "li"
},
"3": {
"id": "3",
"name": "张三"
}
}

第三步 创建server.js文件

1
2
3
4
5
6
7
8
9
10
11
12
import express from 'express'

const app = express()
const port = 3000

app.get('/', (req, res) => {
res.send('Hello!');
});

let server = app.listen(port, function () {
console.log('server Start, Listening on 3000端口');
});

命令行运行:nodemon –exec babel-node –presets=es2015 server.js,
浏览器输入:http://localhost:3000 。显示 Hello!,说明你的 express 服务已经 OK 了。

第四步 编写GraphQL Schema

创建文件夹 /src/schema,并创建文件 index.js。
并在 index.js 文件中编写GraphQL Schema。Schema 是 GraphQL 请求的入口,用户请求的
GraphQL 将会对应到具体的 Schema。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import {
GraphQLObjectType,
GraphQLSchema,
GraphQLString,
GraphQLInt
} from 'graphql'

// 我们要用的模拟数据
const data = require('../../data/data.json')

const User = new GraphQLObjectType({
name: 'User',
description: 'User对象',
fields: {
id: {
type: GraphQLInt
},
name: {
type: GraphQLString
},
}
});

const Query = new GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: User,
args: {
id: {
type: GraphQLInt
}
},
resolve: function (_, args) {
return data[args.id];
}
}
}
});

const Schema = new GraphQLSchema({
query: Query
});

export default Schema;

第五步 连接 schema

修改 server.js 文件,连接 schema。将 server.js 文件修改为以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import express from 'express'
import Schema from './src/schema'
import graphqlHTTP from 'express-graphql'

const app = express()
const port = 3000

app.use('/', graphqlHTTP({
schema: Schema,
graphiql: true
}));

let server = app.listen(port, function () {
console.log('server Start, Listening on 3000端口');
});

这时,在浏览器中可以查看到如下界面:

在左边的空白处输入:

1
2
3
4
5
{
user(id: 1) {
name
}
}

点击“运行”按钮,右边会出现结果:

1
2
3
4
5
6
7
{
"data": {
"user": {
"name": "悬笔e绝"
}
}
}

总结

至此我们对GraphQL有了初步的认识,并且搭建了一个简单的node端的 GraphQL server
更深入的了解,可以查阅官方文档,连接如下:

GraphQL官方文档

分享到:
Disqus 加载中...

如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理