GraphQL是一种新的Api请求规范,它可以让客户端在请求中指定希望得到的数据,而不像REST那样只能在服务端进行预定义。GraphQL 的操作类型可以是 query、mutation 和subscription,描述客户端希望进行什么样的操作:
- query 查询:获取数据
- mutation 变更:对数据进行变更,比如增加、删除、修改
- substription 订阅:当数据发生更改,进行消息推送
GraphQL入门
GraphQL为了方便人们学习,提供了Graphpack用来搭建零配置服务器,Graphpack会读取并发布src目录下的.graphql和resolvers.js。
mkdir graphql-demo
cd graphql-demo
npm install
npm install --save-dev graphpack
// 打开package.json 输入
"scripts": {
"dev": "graphpack",
"build": "graphpack build"
}
// 打开src/schema.graphql,输入
type Query {
hello: String
}
// 打开src/resolvers.js,写入以下代码:
const resolvers = {
Query: {
hello: () => "Hello World!"
}
};
export default resolvers;
然后在graphql-demo目录下执行npm run dev
,打开http://localhost:4000
就可以看到 GraphQL Playground。GraphQL Playground是GraphQL提供的前端页面,封装了http request。GraphQL Playground左边是输入,中间有个执行按钮,点击执行按钮会将我们输入的内容放入http request body发送给我们刚启动的服务器,进过GraphQL解析会执行相应的resolvers函数,GraphQL Playground获取到请求结果并在右边呈现。
之前我们在服务器端定义了Query类型的hello和Query类型的resolver解析函数hello,那我们要怎么获取到返回值Hello World!
呢?在GraphQL Playground左边输入并执行以下代码:
// 输入
query {
hello
}
// 输出
{
"data": {
"hello": "Hello world!"
}
}
更多关于GraphQL的使用可以阅读GraphQL文档来学习。下面提供了query、mutation 和subscription的例子,供大家参考学习。
// db.js
export let users = [
{ id: 1, name: "John Doe", email: "john@gmail.com", age: 22 },
{ id: 2, name: "Jane Doe", email: "jane@gmail.com", age: 23 }
];
// schema.graphql
type Query {
hello: String
users: [User!]!
user(id: ID!): User!
}
type User {
id: ID!
name: String!
email: String!
age: Int
}
type Mutation {
createUser(id: ID!, name: String!, email: String!, age: Int): User!
updateUser(id: ID!, name: String, email: String, age: Int): User!
}
type Subscription {
subsUser(id: ID!): User
}
// resolvers.js
import { users } from "./db";
const {PubSub, withFilter} = require('apollo-server')
const pubsub = new PubSub()
const USER_UPDATE_CHANNEL = 'USER_UPDATE'
const resolvers = {
Query: {
hello: () => "Hello World!",
user: (parent, { id }, context, info) => {
return users.find(user => user.id == id);
},
users: (parent, args, context, info) => {
return users;
}
},
Mutation: {
createUser: (parent, { id, name, email, age }, context, info) => {
const newUser = { id, name, email, age };
users.push(newUser);
return newUser;
},
updateUser: (parent, { id, name, email, age }, context, info) => {
let newUser = users.find(user => user.id === parseInt(id));
newUser.name = name;
newUser.email = email;
newUser.age = age;
pubsub.publish(USER_UPDATE_CHANNEL, {subsUser: newUser})
return newUser;
}
},
Subscription: {
subsUser: {
subscribe: withFilter(
(parent, {id}) => pubsub.asyncIterator(USER_UPDATE_CHANNEL),
(payload, variables) => payload.subsUser.id === parseInt(variables.id)
),
resolve: (payload, variables) => {
console.log('🚢 接收到数据: ', payload)
return payload.subsUser
}
}
}
};
export default resolvers;
测试例子
Query测试例子
// hello
query {
hello
}
// users(GraphQL返回值)
query {
users {
id
name
}
}
// user (GraphQL参数)
query {
user(id: 1) {
id
name
}
}
Mutation测试例子
// 可执行query users查看结果
mutation {
createUser(id: 3, name: "xeon", email: "xeon@qq.com", age: 22) {
id
name
email
age
}
}
Subscription测试例子
在GraphQL Playground左上角新建两个tab,一个输入下面代码并执行,用于监听和相应数据更新
subscription {
subsUser(id: 2) {
id
name
email
}
}
另一个tab执行更新操作
mutation {
updateUser(id: 2, name: "xeon1", email: "test@qq.com") {
id
name
email
}
}
GraphQL与REST
GraphQL在后端上是MVC中的Controller,用于定义API接口的参数和返回值,这似乎是和REST是一种竞争关系,而GraphQL提出“一切皆是图”口号也好像是在挑战REST提出的“一切都是资源“。但是冷静下来想一想,图也是一种资源
,与其说GraphQL是颠覆REST的一种新技术,我更认可GraphQL是对REST的封装的说法,因为GraphQL在实现上主要干了两件事:
- 提供了一个Api,用于接收请求数据
- 根据
schema.graphql
解析请求数据,调用resolvers函数
技术层面上GraphQL是对REST的封装,在设计体验上GraphQL确实给我们带来了许多惊喜。就像文章开题所说的,GraphQL明显比REST更加灵活,在前后端交互上GraphQL也有许多明显的优势:
- 减少HTTP请求,GraphQL可将业务模式自由组合,我们可以调用一次API就能获取到我们想要的数据
- 提升HTTP请求速度,GraphQL可以指定需要的数据,避免传输无效的数据
- 代码复用率高,减少后端代码量,特别是面向用户体验的API
- GraphQL的Subscription封装了管道技术,更方便的实时交互
- 减少前后端开发人员的沟通成本
···
BFF下GraphQL的地位
GraphQL在中台架构和BFF开发模式上的地位举足轻重。前端开发者选择GraphQL开发BFF层的无数种理由:
- GraphQL的类型系统和类型语言与前端的TS不谋而合,前端开发者对GraphQL的学习成本并不高
- GraphQL提供了多种后端语言库,这让GraphQL可以很好的对接各种模式的业务中台
- GraphQL与REST兼容,可以从REST平稳地过度到GraphQL
- 虽然GraphQL建议我们把业务建模为图,但是这并不意味着GraphQL与关系型数据库是相悖的。“图”是通过GraphQL的类型语言来构建的,与Model层无关,更与数据库无关,因此GraphQL可以很好的兼容关系型数据库