三种基本操作 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.
![img_ef17567c3f9f4929ceab1fbba5152594.png](https://i-blog.csdnimg.cn/blog_migrate/9a1d85aeb0498832de885c130ecd16c4.png)
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")
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;
注意点:
上篇讲过GraphQL只是一套规范,目前官方只提供了JS版本,其他PHP,Go等其他语言都是社区实现的。
-
当只有一种操作,并且是query,可以省去query操作名关键字
image.png 可以看到字段和字段参数要指定类型,因为GraphQL是强类型的。
因为指定了参数类型是string,输入时必须要用双引号
注意看调试面板的请求
GraphQL API server运行时,只要构造http请求就可以,传入不同的query参数,也能得到和在GraphiQL同样的结果
![img_b61b665c7ab13a4f2704d296087f4949.png](https://i-blog.csdnimg.cn/blog_migrate/92750bbc4b40e28ea0b762f9ae513c4a.png)
2. 查询
2.1 变量
所有声明的变量都必须是标量、枚举型或者输入对象类型。
![img_3550dc9258a1f6d443d074b9dbb5b174.png](https://i-blog.csdnimg.cn/blog_migrate/356b015f0e987e8dde33d37d6cde5e97.png)
默认变量
使用默认值
![img_46b0b856bb15dd566dbb82067c3247d0.png](https://i-blog.csdnimg.cn/blog_migrate/27a49ec7e980f926c5e145362b2536e8.png)
![img_4277b8263177616f419b1f29cc0a60bf.png](https://i-blog.csdnimg.cn/blog_migrate/f469dab6e394b1fcf8e10312e7e42ccc.png)
覆盖默认值
![img_ccd52e7b29ace7282018bec1ae57d97e.png](https://i-blog.csdnimg.cn/blog_migrate/1eda08ee83bc59197a41c7e4da13bc90.png)
类型后面带个感叹号表示参数必填
![img_90ef5af5ef6ad202d4cef0be3d7a8c06.png](https://i-blog.csdnimg.cn/blog_migrate/5875381cfbcf94a571f68823de8cd6f0.png)
2.2 别名
![img_0bfab3892f6204d4efb07cdb25195013.png](https://i-blog.csdnimg.cn/blog_migrate/c4f86cb4c59e4c6e80cfe888a08e3b43.png)
2.3 片段
提取公众的部分
上面的查询,将共同的字段:id和name,提取成fragment
![img_d77a5948d744b99b71f6a27669f445ee.png](https://i-blog.csdnimg.cn/blog_migrate/c560a7545307e31a154e77bf9767a8bf.png)
2.4 指令
GraphQL内置两个核心指令,@skip
和 @include
@skip(if: Boolean)
如果参数为 true,跳过此字段。
? 貌似参数必须要写默认值 ?
![img_5aea95bf6a5725d358e40169826323bb.png](https://i-blog.csdnimg.cn/blog_migrate/75f3ee54a368b6be4ddb8227b223af00.png)
![img_5830558c6ffd65dc38a124ea88dcddb8.png](https://i-blog.csdnimg.cn/blog_migrate/c6495f08c3364935f7edb5d7beea088f.png)
3. 修改 Mutation
4. 分页
分页的原理:定义一个Edges类型,包含node和cursor字段,Node保存查询列表内容,Cursor记录分页。以下面的Github例子
5. GitHub GraphQL API
打开 https://developer.github.com/v4/explorer/
先打开右侧的Docs浏览所有Query,发现有个名为search的query
![img_67531cdde1e790a0b22b507e214b4180.png](https://i-blog.csdnimg.cn/blog_migrate/6d38f58cd46f781294735cb49b3e986c.png)
他返回的是个 SearchResultItemConnection!类型,接着点进去
![img_a8a9a3749843fdcf100d18611687ffd7.png](https://i-blog.csdnimg.cn/blog_migrate/cdde55a9bfcbb995f168e111d6408f7b.png)
你会发现所有已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
}
}
返回的结果类似
{
"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
}
}
}
- 每个node包含一个cursor游标,不是数字,是唯一字符串
- 如果想查下一页,直接修改query search,添加after参数。
search(first: 3, after:"Y3Vyc29yOjM=", query: "graphql", type: REPOSITORY) {
- 关于实现原理,参考
最后欢迎 clone 我的仓库, 里面包含了所有例子。