三种基本操作 Query(a read‐only fetch), Mutation(a write followed by a fetch), Subscription(订阅,不常用,适合长连接业务)
# 1. 起步,用express实现最简单的例子
运行一个GraphQL API Server
GraphiQL is a great tool for debugging and inspecting a server, so we recommend running it whenever your application is in development mode.
index.js
const express = require('express');
const graphqlHTTP = require('express-graphql');
const schema = require('./schema');
const app = express();
// 具体选项含义见文档
// https://github.com/graphql/express-graphql
app.use('/graphql', graphqlHTTP({
schema: schema,
graphiql: true,
}));
app.listen(12580);
console.log("please open http://localhost:12580/graphql")
2
3
4
5
6
7
8
9
10
11
12
13
schema.js
const {
GraphQLObjectType,
GraphQLSchema,
GraphQLString,
} = require('graphql');
const Query = new GraphQLObjectType({
name: 'BlogSchema',
description: 'Root of the Blog Schema',
fields: () => ({
echo: {
type: GraphQLString,
description: '回应你输入的内容',
args: {
message: {type: GraphQLString}
},
resolve: function(source, {message}) {
return `hello: ${message}`;
}
},
})
});
const Schema = new GraphQLSchema({
query: Query,
});
module.exports = Schema;
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
注意点:
上篇讲过GraphQL只是一套规范,目前官方只提供了JS版本 (opens new window),其他PHP,Go等其他语言都是社区实现的。
当只有一种操作,并且是query,可以省去query操作名关键字
可以看到字段和字段参数要指定类型,因为GraphQL是强类型的。
因为指定了参数类型是string,输入时必须要用双引号
注意看调试面板的请求
GraphQL API server运行时,只要构造http请求就可以,传入不同的query参数,也能得到和在GraphiQL同样的结果
# 2. 查询
# 2.1 变量
所有声明的变量都必须是标量、枚举型或者输入对象类型。
# 默认变量
使用默认值
覆盖默认值
类型后面带个感叹号表示参数必填
# 2.2 别名
# 2.3 片段
提取公众的部分
上面的查询,将共同的字段:id和name,提取成fragment
# 2.4 指令
GraphQL内置两个核心指令,@skip
和 @include
@skip(if: Boolean)
如果参数为 true,跳过此字段。
? 貌似参数必须要写默认值 ?
# 3. 修改 Mutation
# 4. 分页
分页的原理:定义一个Edges类型,包含node和cursor字段,Node保存查询列表内容,Cursor记录分页。以下面的Github例子
# 5. GitHub GraphQL API
打开 https://developer.github.com/v4/explorer/
先打开右侧的Docs浏览所有Query,发现有个名为search的query
他返回的是个 SearchResultItemConnection!类型,接着点进去
你会发现所有已Connection结尾的类型,其结果都包含pageInfo, edges, nodes
输入下面的内容,这个查询是返回包含"graphql"关键字的前三个仓库,并显示每个仓库的前3个issues的作者,头像信息。
{
search(first: 3, query: "graphql", type: REPOSITORY) {
codeCount
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
edges {
cursor
node {
... on Repository {
nameWithOwner
issues(first: 3) {
nodes {
author {
avatarUrl
login
resourcePath
url
}
title
}
}
}
}
}
}
rateLimit {
cost
limit
nodeCount
resetAt
remaining
}
}
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
返回的结果类似
{
"data": {
"search": {
"codeCount": 16287,
"pageInfo": {
"startCursor": "Y3Vyc29yOjE=",
"endCursor": "Y3Vyc29yOjM=",
"hasNextPage": true,
"hasPreviousPage": false
},
"edges": [
{
"cursor": "Y3Vyc29yOjE=",
"node": {
"nameWithOwner": "facebook/graphql",
"issues": {
"nodes": [
{
"author": {
"avatarUrl": "https://avatars0.githubusercontent.com/u/540892?v=4",
"login": "raymondfeng",
"resourcePath": "/raymondfeng",
"url": "https://github.com/raymondfeng"
},
"title": "Possibility of collaboration with LoopBack framework?"
},
{
"author": {
"avatarUrl": "https://avatars1.githubusercontent.com/u/825073?v=4",
"login": "luisobo",
"resourcePath": "/luisobo",
"url": "https://github.com/luisobo"
},
"title": "Pagination?"
},
{
"author": {
"avatarUrl": "https://avatars3.githubusercontent.com/u/71047?v=4",
"login": "KyleAMathews",
"resourcePath": "/KyleAMathews",
"url": "https://github.com/KyleAMathews"
},
"title": "Custom Sorting"
}
]
}
}
},
{
"cursor": "Y3Vyc29yOjI=",
"node": {
"nameWithOwner": "graphql-go/graphql",
"issues": {
"nodes": [
{
"author": {
"avatarUrl": "https://avatars0.githubusercontent.com/u/78585?v=4",
"login": "sogko",
"resourcePath": "/sogko",
"url": "https://github.com/sogko"
},
"title": "Suggestion: Improve package discovery"
},
{
"author": {
"avatarUrl": "https://avatars2.githubusercontent.com/u/1064547?v=4",
"login": "ptomasroos",
"resourcePath": "/ptomasroos",
"url": "https://github.com/ptomasroos"
},
"title": "Why not wrap the C lib?"
},
{
"author": {
"avatarUrl": "https://avatars0.githubusercontent.com/u/1000404?v=4",
"login": "chris-ramon",
"resourcePath": "/chris-ramon",
"url": "https://github.com/chris-ramon"
},
"title": "Using graphql-go in other programs throws various errors."
}
]
}
}
},
{
"cursor": "Y3Vyc29yOjM=",
"node": {
"nameWithOwner": "Youshido/GraphQL",
"issues": {
"nodes": [
{
"author": {
"avatarUrl": "https://avatars2.githubusercontent.com/u/2429244?v=4",
"login": "mrbarletta",
"resourcePath": "/mrbarletta",
"url": "https://github.com/mrbarletta"
},
"title": "How to manage a List of Posts"
},
{
"author": {
"avatarUrl": "https://avatars2.githubusercontent.com/u/2429244?v=4",
"login": "mrbarletta",
"resourcePath": "/mrbarletta",
"url": "https://github.com/mrbarletta"
},
"title": "No way to get requested fields of the object inside `resolve`"
},
{
"author": {
"avatarUrl": "https://avatars0.githubusercontent.com/u/971254?v=4",
"login": "larswolff",
"resourcePath": "/larswolff",
"url": "https://github.com/larswolff"
},
"title": "Minor documentation issue?"
}
]
}
}
}
]
},
"rateLimit": {
"cost": 1,
"limit": 5000,
"nodeCount": 12,
"resetAt": "2018-03-31T01:47:34Z",
"remaining": 4995
}
}
}
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
- 每个node包含一个cursor游标,不是数字,是唯一字符串
- 如果想查下一页,直接修改query search,添加after参数。
search(first: 3, after:"Y3Vyc29yOjM=", query: "graphql", type: REPOSITORY) {
- 关于实现原理,参考 (opens new window)
最后欢迎 clone 我的仓库 (opens new window), 里面包含了所有例子。