GraphQL介绍
- GraphQL是Facebook开发的一种数据查询语言,并于2015年公开发布。它是REST API的替代品。
- GraphQL既是一种用于API的查询语言也是一个满足你数据查询的运行时。GraphQL对你的API的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让API更容易地随着时间推移而演进。
- 官网:https://graphql.org/
- 中文网:https://graphql.cn/
- 特点:
1.请求需要的数据,不多不少
例如:account中有name,age,sex,department等,可以只取得需要的字段。
2.获取多个资源,只用一个请求
3.描述所有可能类型的系统。便于维护,根据需求平滑演进,添加或者隐藏字段。
例如一个查询请求:
//GraphQL语法,后面会讲到
//会根据字段进行数据的获取,而不像REST直接将所有的字段进行返回,这是一个特别好的前端优化的点
query{
getAccount(userId:1){
name //graphql语法会根据里面的字段取出对应的值
age
sex
}
}
结果如下:
{
“data”:{
"getAccount":{
"name":"xxx",
"age":"xxx",
"sex":"xxx"
}
}
}
GraphQL与restful对比
- restful:Representational State Transfer表属性状态转移。本质上就是用定义uri,通过api接口来取得资源。通用系统架构,不受语言限制。restful还是主流。
- 例子:饿了么接口
- restful一个接口只能返回一个资源,graphql一次可以获取多个资源。
- restful用不同的url来区分资源,graphql用类型区分资源。
使用express+GraphQL
- npm init -y //初始化一个package.json文件
- npm i express graphql express-graphql -S
- node helloWorld.js
- localhost:4000/graphql
- query{hello}
const express = require('express');
const graphqlHTTP = require('express-graphql');
const {buildSchema} = require('graphql');
//Construct a schema,using GraphQL,schema language
//构建schema,这定义查询的语句和类型
const schema= buildSchema(
type Account{
name:String
age:Int
sex:String
}
type Query{
hello:String
accountName:String
account:Account
}
);
//The root proyides a resolver function for each API
//定义查询所对应的resolver,也就是查询对应的处理器
const root={
hello:()=>{
return 'Hello World!';
},
accountName:()=>{
return "test name";
};
account:()=>{
return {
name:"test name",
age:11,
sex:"test sex",
}
}
};
const app = express();
app.use('/graphql',graphqlHTTP({
schema:schema,
rootValue:root,
graphql:true, //是否启用调试界面,false就不能调试了,开发时是true,上线的话变为false
}))
app.listen(4000);
类型定义与返回值不一致时会报错
基本参数类型
- 基本类型:String/Int/Float/Boolean/ID。可以在schema声明的时候直接使用。
- [类型]代表数组,例如:[Int]代表整型数组。
参数传递
- 和js传递参数一样,小括号内定义形参,但是注意:参数需要定义类型。
- !(叹号)代表参数不能为空
type Query{
rollDice(numDice:Int!,numSides:Int):[Int]
}
Test Demo:
自定义参数类型
- GraphQL允许用户自定义参数类型,通常用来描述要获取的资源的属性。
type Account{
name:String
age:Int
sex:String
salary(city:String):Int
}
type Query{
account(name:String):Account //定义一个Account属性,然后在Account类型中进行返回
}
GraphQL clients
- 如何在客户端访问graphql的接口?
var username = 66;
//固定写法,Account($username:Int!),account(username:$username),$username和后端的username是一样的
var query = `query Account($username:Int!){
account(username:$username)
}`;
fetch('/graphql',{
method:'POST',
headers:{
'Content-Type':'application/json',
'Accept':'application/json',
},
//因为是post,数据从body出去
body:JSON.stringify({
query,
variables:{username},
})
})
.then(r=>r.json())
.then(data=>console.log('data returned',data));
后端代码
const express = require('express');
const {buildSchema} = require('graphql');
const graphql = require('express-graphql');
//定义schema,查询和类型
const schema = buildSchema(`
type Account{
name:String
age:Int
sex:String
department:String
salary(city:String):Int
}
type Query{
getClassMates(classNo:Int!):[String]
account(username:String):Account
}
`)
//定义查询对应的处理器
const root={
getClassMates({classNo}){
const obj={
31:['name1','name2','name3'],
32:['name4','name5','name6']
}
return obj[classNo];
},
account({username}){
const name=username;
const sex='man';
const age=11;
const department='testdepartment';
const salary=({city})=>{
if(city==='北京'||city==='上海'||city==='广中'||city==='深圳'){
return 10000;
}
return 2000;
}
return {
name,
sex,
age,
department,
salary
}
}
}
const app=express();
app.use('/graphql',graphqlHTTP({
schema:schema,
rootValue:root,
graphiql:true
}))
//公开文件夹,供用户访问静态资源
app.use(express.static("public")
app.listen(3000);
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<button onclick="getData()">获取数据</button>
</body>
</html>
<script>
function getData(){
const query=`
query Account($username:String,$city:String){
account(username:$username){
name
age
sex
salary(city:$city)
}
}
`
const variables={username:"lisi",city:'深圳'}
fetch('/graphql',{
method:'POST',
header:{
'Content-Type':'application/json',
'Accept':'application/json'
},
body:JSON.stringify({
query:query,
variables:variables
})
}).then(res=>res.json)
.then(data=>{
console.log(data);
})
}
</script>
使用Mutations修改数据
- 查询使用query,修改数据使用Mutation
`
//input表示输入
input AccountInput{
name:String
age:Int
sex:String
department:String
salary:Int
}
type Mutation{
//括号里是形参
createAccount(input:AccountInput):Account
updateAccount(id:ID!,input:AccountInput):Account
}
代码
小坑:
- 写mutation的话必须写query
const express = require('express');
const {buildSchema}=require('graphql');
const graphqlHTTP=require('express-graphql');
//定义schema,查询和类型,mutation
const schema=buildSchema(`
input AccountInput{
name:String
age:Int
sex:String
department:String
}
type Account{
name:String
age:Int
sex:String
department:String
}
type Mutation{
createAccount(input:AccountInput):Account
updateAccount(id:ID!,input:AccountInput):Account
}
type Query{
account:[Account]
}
`)
const fakeDB={};
//定义查询对应的处理器
const root={
account(){
var arr=[];
for(const key in fakeDB){
arr.push(fakeDB[key])
}
return arr;
},
createAccount({input}){
//相当于数据库的保存
fakeDB[input.name]=input;
//返回保存结果
return fakeDB[input.name];
},
updateAccount({id,input}){
//相当于数据库的更新
const updateAccount=Object.assign({},fakeDB[id],input);
fakeDB[id]=updateAccount;
//返回数据库
return updatedAccount;
}
}
const app=express();
app.use('/graphql',graphqlHTTP({
schema:schema,
rootValue:root,
graphiql:true
}))
app.listen(3000)
认证与中间件(高级部分)
const express = require('express');
const {buildSchema}=require('graphql');
const graphqlHTTP=require('express-graphql');
//定义schema,查询和类型,mutation
const schema=buildSchema(`
input AccountInput{
name:String
age:Int
sex:String
department:String
}
type Account{
name:String
age:Int
sex:String
department:String
}
type Mutation{
createAccount(input:AccountInput):Account
updateAccount(id:ID!,input:AccountInput):Account
}
type Query{
account:[Account]
}
`)
const fakeDB={};
//定义查询对应的处理器
const root={
account(){
var arr=[];
for(const key in fakeDB){
arr.push(fakeDB[key])
}
return arr;
},
createAccount({input}){
//相当于数据库的保存
fakeDB[input.name]=input;
//返回保存结果
return fakeDB[input.name];
},
updateAccount({id,input}){
//相当于数据库的更新
const updateAccount=Object.assign({},fakeDB[id],input);
fakeDB[id]=updateAccount;
//返回数据库
return updatedAccount;
}
}
const app=express();
//增加一个判断权限的中间件
const middleware=(req,res,next)=>{
if(req.url.indexOf('/graphql')!=-1&&req.headers.cookie.indexOf('auth')){
res.send(JSON.stringify({
error:"您没有权限访问这个接口"
}));
return;
}
next();
}
//使用中间件
app.use(middleware)
app.use('/graphql',graphqlHTTP({
schema:schema,
rootValue:root,
graphiql:true
}))
app.listen(3000)
Constructing Types(高级部分)
- 使用GraphQLObjectType定义type(类型)
意义:方便后期维护
- 使用GraphQLObjectType定义query(查询)
3. 创建schema
高级_与数据库结合实战
const express = require('express');
const {
buildSchema
} = require('graphql');
const graphqlHTTP = require('express-graphql');
const mysql = require('mysql');
// https://www.npmjs.com/package/mysql
var pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
user: 'root',
password: 'root',
database: 'xxxx'
})
//定义schema,查询和类型,mutation
const schema = buildSchema(`
input AccountInput{
name:String
age:Int
sex:String
department:String
}
type Account{
name:String
age:Int
sex:String
department:String
}
type Mutation{
createAccount(input:AccountInput):Account
deleteAccount(id:ID):Boolean
updateAccount(id:ID!,input:AccountInput):Account
}
type Query{
account:[Account]
}
`)
const fakeDB = {};
//定义查询对应的处理器
const root = {
account() {
return new Promise((resolve,reject)=>{
pool.query('select name,age,sex,department from account',(err,results)=>{
if(err){
console.log('error',err.message);
return;
}
const arr=[];
for(const i=0;i<results.length;i++){
arr.push({
name:results[i].name,
sex:results[i].sex,
age:results[i].age,
department:results[i].department,
})
}
resolve(results);
})
})
},
createAccount({
input
}) {
const data = {
name: input.name,
sex: input.sex,
age: input.age,
department: input.department
}
return new Promise((resolve, reject) => {
pool.query('insert into accout set ?', data, (err) => {
if (err) {
console.log('error',err.message);
return;
}
//返回保存结果
resolve(data);
})
})
},
updateAccount({
id,input
}) {
const data=input;
return new Promise((resolve,reject)=>{
pool.query('update account set?where name=?',[data,id],(err)=>{
if(err){
console.log('err',err.message);
return;
}
resolve(data);
})
})
},
deleteAccount({id}){
return new Promise((resolve,reject)=>{
pool.query('delete account where name=?',[id],(err)=>{
if(err){
console.log("err",err.message)
reject(false);
return;
}
resolve(true);
})
})
}
}
const app = express();
//增加一个判断权限的中间件
const middleware = (req, res, next) => {
if (req.url.indexOf('/graphql') != -1 && req.headers.cookie.indexOf('auth')) {
res.send(JSON.stringify({
error: "您没有权限访问这个接口"
}));
return;
}
next();
}
//使用中间件
app.use(middleware)
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true
}))
app.listen(3000)
学习视频:https://www.bilibili.com/video/av46200333?from=search&seid=9265460496338796424