Open lei4519 opened 5 months ago
2023-10-27
本篇文章不会去过多的讨论技术细节,只是从核心概念上去说明 GraphQL 解决了什么问题,是如何怎么做到的
现在只需要知道,对于前端来说,它是一种资源请求方式,可以用来代替 RESTful 风格的 API 请求。
那为什么要去代替 RESTful 的 API 请求呢?因为遇到了一些问题
前端的多个功能可能会依赖一份数据源的不同部分
把需要的字段传进去
把表名传进去
重构传输结构
数据源:数据库、redis、ES、RPC 等等
数据处理:多数情况从数据源拿到的数据,还需要进行逻辑处理才会返回
子查询:先执行父级的查询,再用父级的信息查找子级
封装、分层
拿到的数据和 API 文档不一致
可不可以如果返回的和需要的不一致,就直接报错,接口 500,这样可以更快的定位问题(更好的甩锅)
问题:
GraphQL | A query language for your API
从 resolves 中找到对应 type 的各字段解析方法,如果字段的类型不是标量类型(基础类型),就递归对 type 进行解析
resolves
type
内置标量类型:Int、Float、String、Boolean、ID
考虑上述代码示例,如果 1 条回答中,有 20 条评论,各 fetch 方法分别会被调用多少次?
fetch
fetchAnswer: 1 fetchReviews: 1 fetchUser: 21
如果我们查询的是 1 个回答列表,列表中有 20 条回答,每个回答有 20 条评论,上述方法有会被调用多少次?
fetchAnswer: 1 fetchReviews: 20 fetchUser: 420
遇事不决加抽象
简单实现
const arr = new Set() let flag = true const fetchUser = (user_id) => { arr.add(user_id) if (flag) { flag = false setTimeout(() => { batch_fetch_user([...arr]) arr.clear() flag = true }) } }
封装
class Loader { constructor(callback) { this.arr = new Set() this.flag = true this.callback = callback } load(key) { this.arr.add(key) if (this.flag) { this.flag = false setTimeout(() => { this.callback([...this.arr]) this.arr.clear() this.flag = true }) } } }
const DataLoader = require("dataloader") const userLoader = new DataLoader((ids) => { console.log(ids) // `SELECT * FROM user WHERE id IN (${ids.join(',')})` // rpc.client.fetchUsers(ids) return Promise.resolve(["123"]) }) Array(20) .fill(1) .map((_, i) => userLoader.load(i)) Array(20) .fill(1) .map((_, i) => userLoader.load(0))
重复的写 schema 和 类型(结构)定义,并且两者又是很像的
GraphQL Schema
type Answer { id: Int content: String }
Go
type Answer struct { id int content string }
Rust
struct Answer { id: i32 content: string }
TS
class Answer { id: Number content: string }
先写 schema,生成类型(结构)
先写类型(结构),生成 schema
本篇文章不会去过多的讨论技术细节,只是从核心概念上去说明 GraphQL 解决了什么问题,是如何怎么做到的
GraphQL 设计思路
GraphQL 是什么?
现在只需要知道,对于前端来说,它是一种资源请求方式,可以用来代替 RESTful 风格的 API 请求。
那为什么要去代替 RESTful 的 API 请求呢?因为遇到了一些问题
数据获取
前端的多个功能可能会依赖一份数据源的不同部分
按需获取字段
把需要的字段传进去
获取不同表的字段
把表名传进去
同时查询不同的表
重构传输结构
不同的数据源 & 数据处理
数据源:数据库、redis、ES、RPC 等等
数据处理:多数情况从数据源拿到的数据,还需要进行逻辑处理才会返回
嵌套查询 / 子查询
子查询:先执行父级的查询,再用父级的信息查找子级
重构传输结构
封装、分层
数据校验
拿到的数据和 API 文档不一致
可不可以如果返回的和需要的不一致,就直接报错,接口 500,这样可以更快的定位问题(
更好的甩锅)问题:
类型定义
总结
GraphQL 官方设计
GraphQL | A query language for your API
类型定义
内置根类型
字段解析
从
resolves
中找到对应type
的各字段解析方法,如果字段的类型不是标量类型(基础类型),就递归对type
进行解析N + 1 问题
考虑上述代码示例,如果 1 条回答中,有 20 条评论,各
fetch
方法分别会被调用多少次?如果我们查询的是 1 个回答列表,列表中有 20 条回答,每个回答有 20 条评论,上述方法有会被调用多少次?
问题
遇事不决加抽象
简单实现
封装
DataLoader
总结
Code First Schema
类型语言的问题
重复的写 schema 和 类型(结构)定义,并且两者又是很像的
GraphQL Schema
Go
Rust
TS
Schema First
先写 schema,生成类型(结构)
Code First
先写类型(结构),生成 schema
调用式
结构式
JS
Go
Rust