GraphQL

GraphQL

GraphQL是Facebook开发的一种数据查询语言,并于2015公开发布,它是rest API的替代品。

GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。

官网:https://graphql.org/

中文网:https://graphql.cn/

请求数据:

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

获取多个资源,只用一个请求

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

描述所有的可能类型系统

GraphQL API 基于类型和字段的方式进行组织,而非入口端点。你可以通过一个单一入口端点得到你所有的数据能力。GraphQL 使用类型来保证应用只请求可能的数据,还提供了清晰的辅助性错误信息。应用可以使用类型,而避免编写手动解析代码。

快步前进强大的开发者工具

不用离开编辑器就能准确知道你可以从 API 中请求的数据,发送查询之前就能高亮潜在问题,高级代码智能提示。利用 API 的类型系统,GraphQL 让你可以更简单地构建如同GraphiQL的强大工具。

API 演进 无需划分版本

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

使用你现有的 数据和代码

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

学习GraphQL

nodejs环境基本信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"name": "code",
"version": "1.0.0",
"description": "",
"main": "helloWorld.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.16.4",
"express-graphql": "^0.7.1",
"graphql": "^14.0.2",
"mongoose": "^5.13.14"
}
}

1.基本使用

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
46
47
const express = require("express")
const {buildSchema} = require("graphql");
const graphqlHttp = require("express-graphql")
// 定义schema类型
var Schema = buildSchema(`
type Query{
hello: String,
getName: String,
getAge: Int
}
`)

// 实现schema中的hello、getName、getAge
const root = {
hello:() => {
// 通过数据库查询
var str = "hello world sola"
return str;
},
getName:() => {
// 通过数据库查询
var str = "i am groot"
return str;
},
getAge:() => {
// 通过数据库查询
var age = 12
return age;
}
}

var app = express()

app.use("/home",function (req, res) {
res.send("home data")
})

app.use("/list",function (req, res) {
res.send("list data")
})

app.use("/graphql",graphqlHttp({
schema:Schema, // 绑定schema
rootValue:root, // 绑定root
graphiql:true // 开启graphql调试
}))
app.listen(3000)

2.参数类型与传递

  • 基本类型:String、Int、Float、Boolean和ID。可以在schema声明的时候直接使用

  • [类型]代表数组,例如:[Int]代表整形数组

  • 和JS传递参数一样,小括号定义形参,但是注意:参数需要定义类型

  • !代表参数不能为空

    1
    2
    3
    type Query {
    rollDice(numDice: Int!,numSides:Int):[Int]
    }

当查询需要返回负责数据类型的时候,需要在buildSchema中自定义复杂数据类型,如下,Account、Film为自定义数据类型。

当查询需要传参时,getFilmDetail(id:Int!):Film

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var Schema = buildSchema(`

type Account{
name: String,
age: Int
}
type Film{
id:Int,
name: String,
poster: String
price: Int
}

type Query{
getAccountInfo: Account,
getNowplayingList:[Film],
getFilmDetail(id:Int!):Film
}
`)

示例:

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
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
const express = require("express")
const {buildSchema} = require("graphql");
const graphqlHttp = require("express-graphql")
var Schema = buildSchema(`

type Account{
name: String,
age: Int
}
type Film{
id:Int,
name: String,
poster: String
price: Int
}

type Query{
hello: String,
getName: String,
getAge: Int,
getAllNames:[String],
getAllAges:[Int],
getAccountInfo: Account,
getNowplayingList:[Film],
getFilmDetail(id:Int!):Film
}
`)
var fakeDb = [{
id:1,
name:'111',
poster:"http://1111",
price:100
},{
id:2,
name:'222',
poster:"http://2222",
price:200
},{
id:3,
name:'333',
poster:"http://3333",
price:300
}]
const root = {
hello:() => {
// 通过数据库查询
var str = "hello world"
return str;
},
getName:() => {
// 通过数据库查询
var str = "i am groot"
return str;
},
getAge:() => {
// 通过数据库查询
var age = 12
return age;
},
getAllNames:() => {
return ['a','b','c']
},
getAllAges:() => {
return [1,2,3,4,5]
},
getAccountInfo(){
return {
name:'sola',
age:12
}
},
getNowplayingList(){
return fakeDb
},
getFilmDetail({id}){
console.log(fakeDb.filter(item => item.id === id));
return fakeDb.filter(item => item.id === id)[0]
}
}

var app = express()

app.use("/graphql",graphqlHttp({
schema:Schema,
rootValue:root,
graphiql:true
}))
app.listen(3000)

3.mutation实现增删改

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
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
const express = require("express")
const {buildSchema} = require("graphql");
const graphqlHttp = require("express-graphql")


var Schema = buildSchema(`
type Film{
id:Int,
name: String,
poster: String
price: Int
}

input FilmInput{
name: String,
poster: String
price: Int
}

type Query{
getNowplayingList:[Film]
}

type Mutation{
createFilm(input:FilmInput):Film,
updateFilm(id:Int!,input:FilmInput):Film,
deleteFilm(id:Int!):Int
}
`)
var fakeDb = [{
id:1,
name:'111',
poster:"http://1111",
price:100
},{
id:2,
name:'222',
poster:"http://2222",
price:200
},{
id:3,
name:'333',
poster:"http://3333",
price:300
}]
const root = {

getNowplayingList(){
return fakeDb
},
createFilm({input}){
/*
mutation {
createFilm(input:{
name:"test",
poster:"aa",
price:300
}) {
id,
name,
price,
poster
}
}
* */
var obj = {...input,id:fakeDb.length+1}
fakeDb.push(obj)
return obj
},
updateFilm({id,input}){
/*
mutation {
updateFilm(id:3,input:{
name:"修改name",
poster:"修改poster",
price:77777
}) {
id,
name,
poster,
price
}
}
*/
var current = null
fakeDb = fakeDb.map(item=>{
if (item.id === id){
current = {...item,...input}
return current
}
return item
})
return current

},
deleteFilm({id}){
/*
mutation {
deleteFilm(id:2)
}
*/
fakeDb = fakeDb.filter(item =>item.id !== id)
return 1
}
}

var app = express()

app.use("/graphql",graphqlHttp({
schema:Schema,
rootValue:root,
graphiql:true
}))
app.listen(3000)

4.结合数据库(mongo)

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
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
const express = require("express")
const {buildSchema} = require("graphql");
const graphqlHttp = require("express-graphql")

// 连接数据库服务
var mongoose = require("mongoose")
mongoose.connect("mongodb://localhost:27017/maizuo",{ useNewUrlParser: true, useUnifiedTopology: true })
// 限制表的字段类型,及个数
var FilmModel = mongoose.model("film",new mongoose.Schema({
name:String,
poster:String,
price:Number
}))
/*
* FilmModel.create
* FilmModel.find
* FilmModel.update
* FilmModel.delete
*
* */
var Schema = buildSchema(`
type Film{
id:String,
name: String,
poster: String
price: Int
}

input FilmInput{
name: String,
poster: String
price: Int
}

type Query{
getNowplayingList(id:String!):[Film]
}

type Mutation{
createFilm(input:FilmInput):Film,
updateFilm(id:String!,input:FilmInput):Film,
deleteFilm(id:String!):Int
}
`)

const root = {

getNowplayingList({id}){
if (!id) return FilmModel.find()
return FilmModel.find({_id:id})
},
createFilm({input}){
return FilmModel.create({
...input
})
},
updateFilm({id,input}){
return FilmModel.updateOne({
_id:id
},{
...input
}).then(res => FilmModel.find({_id:id})).then(res => res[0])
},
deleteFilm({id}){
return FilmModel.deleteOne({_id:id}).then(res =>1)
}
}

var app = express()

app.use("/graphql",graphqlHttp({
schema:Schema,
rootValue:root,
graphiql:true
}))


//配置静态资源 目录
app.use(express.static("public"))
app.listen(3000)

5.客户端访问

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
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>home</h1>
<button onclick="getData()">查询数据</button>
<button onclick="createData()">创建数据</button>
<button onclick="updateData()">更新数据</button>
<button onclick="deleteData()">删除数据</button>
<script>
function getData() {
var myquery = `
query{
getNowplayingList {
id,
name,
price,
poster
}
}
`
fetch("/graphql",{
method:"post",
headers:{
"Content-Type":"application/json",
"Accept":"application/json"
},
body:JSON.stringify({
query:myquery
})
}).then(res => res.json()).then(res =>console.log(res))
}
function createData() {
var myquery = `
mutation($input:FilmInput){
createFilm(input:$input) {
id,
name
}
}
`
fetch("/graphql",{
method:"post",
headers:{
"Content-Type":"application/json",
"Accept":"application/json"
},
body:JSON.stringify({
query:myquery,
variables:{
input:{
name:"piggy77777",
price:7878787,
poster:"http://123456.com"
}
}
})
}).then(res => res.json()).then(res =>console.log(res))
}
function updateData() {
var myquery = `
mutation($id:String!,$input:FilmInput){
updateFilm(id:$id,input:$input) {
id,
name
}
}
`
fetch("/graphql",{
method:"post",
headers:{
"Content-Type":"application/json",
"Accept":"application/json"
},
body:JSON.stringify({
query:myquery,
variables:{
id:"6267a2634b452527c04c7a65",
input:{
name:"sola is cool",
price:7878787,
poster:"http://123456.com"
}
}
})
}).then(res => res.json()).then(res =>console.log(res))
}
function deleteData() {
var myquery = `
mutation($id:String!){
deleteFilm(id:$id)
}
`
fetch("/graphql",{
method:"post",
headers:{
"Content-Type":"application/json",
"Accept":"application/json"
},
body:JSON.stringify({
query:myquery,
variables:{
id:"6267a2634b452527c04c7a65"
}
})
}).then(res => res.json()).then(res =>console.log(res))
}
</script>
</body>
</html>

6.结合react

npm i react-apollo apollo-boost graphql graphql-tag

1.普通query
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
46
47
48
49
50
51
52
53
import React, {Component} from 'react';
import {ApolloProvider,Query} from 'react-apollo'
import ApolloClient from 'apollo-boost'
import gql from 'graphql-tag'
const client = new ApolloClient({
uri:"/graphql"
})
class App
extends Component {
render() {
return (
<ApolloProvider client={client}>
<div>
<SolaQuery/>
</div>
</ApolloProvider>

);
}
}
class SolaQuery extends Component{
query = gql`
query{
getNowplayingList {
id,
name,
price,
poster
}
}
`
render() {
return <Query query={this.query}>
{
({loading, data}) => {
console.log(data);
return loading? <div>正在加载。。</div> : <div>
{
data.getNowplayingList.map(item => <div key={item.id}>
<div>{item.name}</div>
<div>{item.price}</div>
<div>{item.poster}</div>
</div>
)
}
</div>
}
}
</Query>
}

}
export default App;
2.参数query
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import React, {Component} from 'react';
import {ApolloProvider,Query} from 'react-apollo'
import ApolloClient from 'apollo-boost'
import gql from 'graphql-tag'
const client = new ApolloClient({
uri:"/graphql"
})
class App
extends Component {
render() {
return (
<ApolloProvider client={client}>
<div>
<SolaQuery></SolaQuery>
</div>
</ApolloProvider>

);
}
}
class SolaQuery extends Component{


query = gql`
query getNowplayingList($id:String!){
getNowplayingList(id:$id) {
id,
name,
price,
poster
}
}
`
state= {
id: "6267b2b08c4fba1718ef496f"
}

render() {
return <div>
<input type="text" onChange={(evt)=>{
this.setState({
id:evt.target.value
})
}}/>
<Query query={this.query} variables={{id:this.state.id}}>
{
({loading, data}) => {
console.log(data);
return loading? <div>正在加载。。</div> : <div>
{
data.getNowplayingList.map(item => <div key={item.id}>
<div>{item.name}</div>
<div>{item.price}</div>
<div>{item.poster}</div>
</div>
)
}
</div>
}
}
</Query>
</div>

}

}
export default App;
3.mutation_create
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import React, {Component} from 'react';
import {ApolloProvider,Mutation} from 'react-apollo'
import ApolloClient from 'apollo-boost'
import gql from 'graphql-tag'
const client = new ApolloClient({
uri:"/graphql"
})
class App
extends Component {
render() {
return (
<ApolloProvider client={client}>
<div>
<SolaCreate></SolaCreate>
</div>
</ApolloProvider>

);
}
}
class SolaCreate extends Component{


createFilm = gql`
mutation createFilm($input:FilmInput){
createFilm(input:$input) {
id,
name,
price,
poster
}
}
`
state= {
id: "6267b2b08c4fba1718ef496f"
}

render() {
return <div>
<Mutation mutation={this.createFilm}>
{
(createFilm,{data}) => {
console.log(data);
return <div>
<button onClick={() => {
createFilm({
variables:{
input:{
name:"888",
price:888,
poster:'http://888'
}
}
})
}
}>click</button>
</div>
}
}
</Mutation>
</div>

}

}
export default App;
4.mutation_update
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import React, {Component} from 'react';
import {ApolloProvider,Mutation} from 'react-apollo'
import ApolloClient from 'apollo-boost'
import gql from 'graphql-tag'
const client = new ApolloClient({
uri:"/graphql"
})
class App
extends Component {
render() {
return (
<ApolloProvider client={client}>
<div>
<SolaUpdate></SolaUpdate>
</div>
</ApolloProvider>

);
}
}
class SolaUpdate extends Component{


updateFilm = gql`
mutation updateFilm($id:String!,$input:FilmInput){
updateFilm(id:$id,input:$input) {
id,
name,
price,
poster
}
}
`
state= {
id: "6267b2b08c4fba1718ef496f"
}

render() {
return <div>
<Mutation mutation={this.updateFilm}>
{
(updateFilm,{data}) => {
console.log(data);
return <div>
<button onClick={() => {
updateFilm({
variables:{
id:"6267ba1ba47c570fe062081a",
input:{
name:"888-修改 ",
price:8989,
poster:'http://888修改'
}
}
})
}
}>click</button>
</div>
}
}
</Mutation>
</div>

}

}
export default App;
5.mutation_delete
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
46
47
48
49
50
51
52
53
54
55
56
57
import React, {Component} from 'react';
import {ApolloProvider,Mutation} from 'react-apollo'
import ApolloClient from 'apollo-boost'
import gql from 'graphql-tag'
const client = new ApolloClient({
uri:"/graphql"
})
class App
extends Component {
render() {
return (
<ApolloProvider client={client}>
<div>
<SolaDelete></SolaDelete>
</div>
</ApolloProvider>

);
}
}
class SolaDelete extends Component{


deleteFilm = gql`
mutation deleteFilm($id:String!){
deleteFilm(id:$id)
}
`
state= {
id: "6267b2b08c4fba1718ef496f"
}

render() {
return <div>
<Mutation mutation={this.deleteFilm}>
{
(deleteFilm,{data}) => {
console.log(data);
return <div>
<button onClick={() => {
deleteFilm({
variables:{
id:"6267ba1ba47c570fe062081a"
}
})
}
}>click</button>
</div>
}
}
</Mutation>
</div>

}

}
export default App;