lei4519 / blog

记录、分享
4 stars 1 forks source link

漫谈 GraphQL #56

Open lei4519 opened 5 months ago

lei4519 commented 5 months ago

本篇文章不会去过多的讨论技术细节,只是从核心概念上去说明 GraphQL 解决了什么问题,是如何怎么做到的

GraphQL 设计思路

GraphQL 是什么?

现在只需要知道,对于前端来说,它是一种资源请求方式,可以用来代替 RESTful 风格的 API 请求。

那为什么要去代替 RESTful 的 API 请求呢?因为遇到了一些问题

  1. 数据获取
  2. 数据校验

数据获取

前端的多个功能可能会依赖一份数据源的不同部分

  1. 后端需要频繁的修改字段获取逻辑
  2. 不管前端功能中实际用到了几个字段,接口都会返回所有的字段
    • 对于字段很多的接口,网络传输的效率会有很大差别

按需获取字段

把需要的字段传进去

获取不同表的字段

把表名传进去

同时查询不同的表

重构传输结构

不同的数据源 & 数据处理

数据源:数据库、redis、ES、RPC 等等

数据处理:多数情况从数据源拿到的数据,还需要进行逻辑处理才会返回

嵌套查询 / 子查询

子查询:先执行父级的查询,再用父级的信息查找子级

重构传输结构

封装、分层

数据校验

拿到的数据和 API 文档不一致

可不可以如果返回的和需要的不一致,就直接报错,接口 500,这样可以更快的定位问题(更好的甩锅

问题:

类型定义

总结

GraphQL 官方设计

GraphQL | A query language for your API

类型定义

内置根类型

Query 类型定义 Query 查询

字段解析

resolves  中找到对应  type  的各字段解析方法,如果字段的类型不是标量类型(基础类型),就递归对 type  进行解析

内置标量类型:Int、Float、String、Boolean、ID

N + 1 问题

考虑上述代码示例,如果 1 条回答中,有 20 条评论,各 fetch  方法分别会被调用多少次?

fetchAnswer: 1  
fetchReviews: 1  
fetchUser: 21  

如果我们查询的是 1 个回答列表,列表中有 20 条回答,每个回答有 20 条评论,上述方法有会被调用多少次?

fetchAnswer: 1  
fetchReviews: 20  
fetchUser: 420  

问题

  1. 过多的 IO
  2. 重复请求

遇事不决加抽象

简单实现

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  
      })  
    }  
  }  
}  

DataLoader

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))  
DataLoader Vue

总结

Code First Schema

类型语言的问题

重复的写 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 First

先写 schema,生成类型(结构)

Code First

先写类型(结构),生成 schema

调用式

结构式

JS

lib star modal
graphql-js 19.7k 两种都支持:code first(调用式)
apollo-server 13.5k schema first
type-graphql 7.9k code first(结构式)
graphql-yoga 7.8k schema first
nestjs 60.4k / 1.3k 两种都支持:code first(结构式)

Go

lib star modal
graphql-go 9.5k code first(调用式)
gqlgen 9.3k schema first

Rust

lib star modal
juniper 5.3k code first(结构式)
async-graphql 3k code first(结构式)