Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GraphQL 相关 #79

Open
Pines-Cheng opened this issue Mar 30, 2020 · 0 comments
Open

GraphQL 相关 #79

Pines-Cheng opened this issue Mar 30, 2020 · 0 comments

Comments

@Pines-Cheng
Copy link
Owner

Pines-Cheng commented Mar 30, 2020

  • GraphQL 的核心价值在于一整套的体系、生态、工作流,如:DSL,解析器,调试工具,代码生成等,这些在针对类似的场景或解决方案很有参考意义。

官方文档最棒:GraphQL 入门

缺点:每一个 field 都对数据库直接跑一个 query,会产生大量冗余 query。

GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。

GraphQL 作为 FaceBook 2015 年推出的 API 定义/查询语言,在历经了两年的发展之后,社区已相对发达和完善。目前社区相关的文章已经很多,有兴趣的同学可以去 Google,或者直接看 GraphQL 官方教程 以及 Apollo GraphQL Server 官方文档

概念和定义

GraphQL 的语言

定义了两种语言。分别是:Queries and Mutations 和 GraphQL Schema。

每一个 GraphQL 服务都会定义一套类型,用以描述你可能从那个服务查询到的数据。每当查询到来,服务器就会根据 schema 验证并执行查询。这个简单的语言称之为 “GraphQL schema language” —— 它和 GraphQL 的查询语言很相似,

操作名称(Operation name)

操作类型可以是 query、mutation 或 subscription,描述你打算做什么类型的操作。

解析器(Resolve Functions)

Schema告诉服务端允许客户端进行哪些查询,以及不同类型的关联方式,但是它不包含一条关键信息:每种类型的数据来自何处!这是解析器的作用所在。

解析器指定了Schema中的类型和字段如何连接到各种后端。

生态

工具

GraphiQL 是一个 GraphQL 浏览器 IDE 工具,该仓库同时还包含 GraphQL LSP 及相关生态。

前端

使用 Axios 请求 GraphQL API

Axios 是用于 JavaScript 的 HTTP 客户端库。 如果你正在使用 React、 Vue 或 Angular 构建一个应用程序,使用类似 Apollo Client 的 GraphQL 客户端库是很有用的,但是如果你只需要使用一个 GraphQL 调用,比如来自 Node.js 脚本,你可以使用任何你想要的库。

graphql-tag 用来把 string 转化成 GraphQL 的 AST。既然,在客户端发请求时仍然使用 string,为什么需要客户端转成 AST,这有几个原因

  • 编译成 AST 可以在编译时检确保 query 的合法性 (比如查询了不存在的字段)
  • 可以按照特定条件对多个 query 进行合并,多个请求合并为同一个请求
  • 可以按照客户端缓存对某些字段进行过滤 (skip),避免冗余查询
  • ... 诸多好处

在服务器端也有诸多好处,如

  • 解析出来客户端请求的 field,与数据库一对比,按需请求数据库字段
  • 添加新的 directives
  • ...
// gql.ts
import gql from 'graphql-tag'

export const PLUGINS = gql`
  query($filter: PluginFilter, $pageInfo: PageInfo) {
    res: plugins(filter: $filter, pageInfo: $pageInfo) {
      count
      plugins {
        ${BASE_PLUGIN_FIELDS}
        npmUrl
        gitUrl
        filePath
        starCount
        downloadCount
        commentCount
      }
    }
  }
`
import { print } from 'graphql'

const res = await axios.post(graphqlURL, {
  query: print(PLUGINS),
  variables: {
    pageInfo: {
      limit: 200
    }
  }
})

graphql-tag 的好处我们上面已经提到了。这里 print 的作用是: Converts an AST into a string, using one set of reasonable formatting rules。

不过你也可以选择直接传入: query: print(" XXX ")

后台

以下是服务器响应查询所需的三个主要步骤:

  • 解析(Parse)
  • 验证(Validate)
  • 执行(Execute)

参考:GraphQL - Execution

实践

参考:GraphQL 从入门到实践

当前阻力

GraphQL 已经出来这么久,仍然处于不温不火的阶段,主要存在一下几个阻力(引用自 winter):

  • 现在主流的后端架构,集群间通讯还是http或者rpc的API,固定输入输出字段,根本不能满足GraphQL的需求,要上就都得改
  • GraphQL 的每一个实体背后可能对应着不同的数据库甚至不同类型的存储集群,后端集群间的海量数据自由 join,基本还是无解的,只能搭专门的集群处理,这个不清楚FB是否有什么黑科技,严重怀疑FB自己也没做到全业务查询
  • 新加一个 GraphQL 集群,意味着中心化的流量,现在真正做到中心化统一接入层的企业都很少,而且中心化流量要求巨大的中心化集群,技术上运维上又是一个难题。

Resolve 逐层解析会有一些问题,如果 Resolve 请求数据库,就要用到 DataLoader DataLoader 是 gql 服务器性能的关键一环,也是 gql 社区的主要推动完善方向,就像 react 里面的shouldComponentUpdate 一样制约着 gql 服务器的性能。

在客户端实现一个 API Layer

来自 @kuitos

GraphQL 作为一个标准化并自带类型系统的 API Layer,其工程价值我也不再过多广告了。只是在实践过程中,既然我们无法完全避免服务端与客户端的实体与接口定义重复(使用 apollo-codegen 可以避免一部分),而且对于大部分小团队而言,运维一个 productive nodejs system 实际上都是力有未逮。**那么我们是不是可以考虑在纯客户端构建一个类 GraphQL 的 API Layer 呢?**这样既可以有效的避免编码重复,也能大大的降低对团队的要求,可操作的空间也比增加一个 nodejs 中间层大得多。

其实很简单,你只需要在客户端把 Apollo Server 中要写的 resolvers 写一遍,然后配上一些性能提升手段(如缓存等),你的 API Layer 就完成了。

经过这一层的数据处理,我们就能确保我们的应用运行在前端自己定义的数据模型之下。这样之后后端接口不论是数据结构还是字段名的变更,我们只需要在这一层做简单调整即可,而不会影响到我们上层的业务及视图。相应的,我们的业务层逻辑不再会直接对接接口 url,而是将其隐藏在 API Layer 下,这样不仅能提升业务代码的可读性,也能做到眼不见为净。。。

事实上,对于大部分团队而言,客户端 API Layer 已经够用了,增加一层 GraphQL 并不是那么必要。而且如果没有很好的支持将客户端接口转换成 GraphQL Schema 和 resolver 的工具时,我们并不能很愉快的 coding,毕竟两端重复的工作还是有点多。

参考

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant