GraphQL从入门到实践

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可以很好的兼容关系型数据库
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341