GraphQL 从入门到入土
在入职之后用了很久的 GraphQL,之前总是得其形而不得其神,在这次 API 设计失误之后,我总算领悟到了一点 GraphQL 的设计思想,所以来随便的总结一下。
GraphQL 到底是什么
GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL 并没有和任何特定数据库或者存储引擎绑定,而是依靠你现有的代码和数据支撑。
——官方说明
这里想说明是,GraphQL 是一种 API 查询语言,而 Restful 是一种 API 设计的架构思路,实际上,他们都建立于数据层之上,GraphQL 本身并不是一个 Gateway,许多人都把 GraphQL 当做 Restful API 的 Gateway 层来用,我觉得这是一种非常错误的做法。
前端的 Gateway 层通常都在做一些 SOA 转 Restful 的事情,SOA 转 Restful 实际上是把多个服务的数据拼装在一起,而 Restful 到 GraphQL 更多的是一种字段的筛选,这两者的功能作用是完全不同的,也就完全没有 Restful 到 GraphQL 的必要了。
换句话说,我认为「将 GraphQL 作为 Gateway 层」是一种错误的说法,而「Gateway 层支持转换成 GraphQL 查询」才是正确的说法。
扯远了,这只是一些小问题,还是引入我们正式的入门部分。
GraphQL 入门
关于基本的入门,你可以在官方文档里看到:https://graphql.org/learn/,这里着重摘取的是可能会被忽略的,除了「基础使用」以外的部分。
- Fragments:很多时候我们并用不到 Fragments,因为基本的 Node 可能已经给我们封装好了,我们只需要根据 Node 挑选自己想要的字段,但是如果多个接口挑选的都是相同的字段呢?这就是 Fragments 可以使用到的地方了。
- Directives:GraphQL 甚至还支持简单的逻辑判断,由
@include(if: Boolean)
和@skip(if: Boolean)
来决定某些字段是否存在 - Union Type:与
...on
连用,可以根据内容的类型展示不同的字段,这样就可以不必讲所有内容局限在相同的字段内。
GraphQL 与 Restful API 不同,只有一个 /graphql
的 endpoint,具体是「查询」还是「增删改」,则是由 query
与 mutation
区别的。
用 GraphQL 合作开发
GET /graphql
,一般都会存在 GraphiQL,它会根据后端注册的情况找到所有的 query
/mutation
/node
,并且展示在 doc 上,这样前端和后端就不用靠一些三方的文档工具进行沟通。
同时,在测试接口时,由于是在 domain 上的 /graphql
路由进行测试,所以不用像传统的 Restful 接口一样单独配置 cookie
,前端可以直接进行测试,甚至还有一些 lint 和提示,可以说是非常优雅了。
对于前端来说,这可以说是一种解放生产力的行为,只要使用 apollo
甚至是 fetch
,只要将 GraphQL 的语句传入,然后处理返回值就行了,减少了比较多的沟通成本。
相对的来说,后端从 controller
返回 JSON 字符串,变成了需要定义 Node、Query、Mutation,实际上是增加了不少改造成本的,这大概也就是为什么 GraphQL 一直推广不开的理由了。
使用 GraphQL 的优点
在很多例子中会举例关于 endpoint 过多的例子,比如
/user
/user-with-friends
/user-follows
但是实际上你在开发中会发现:鬼才会这么写接口,在现实生活中,我们往往不会严格的遵照 Restful 的设计规范,但是很多接口我们可能都会用到 user 的信息,如果要挨个调用某些函数去处理 user 的字段,很快你就会麻烦的想死,而如果是在 GraphQL 中,我们只需要定义一个 UserNode
,定义好这个 Node 需要怎么去查询(一般简单点的和一个 Model 关联起来),然后在需要的地方引用这个 Node
,接下来就可以自由的提取需要的字段了。
GraphQL 的设计误区
当然,由于 GraphQL 和 Restful API 有着明显的区别,如果我们还按照旧的模式去写 API,就完全没有用到 GraphQL 上述所说的优点,更有可能造成一些明显的性能问题。
复用 Node
在 GraphQL 中,我们不用像 Restful 一样处理好字段在给前端,而是可以自己由多个 Model 来源的 Node 去让前端组装。比如多种来源的混合内容,在 Restful 中,我们会统一自己处理 type
加上 content
给前端,而在 GraphQL 中,我们可以用 __typename
+ UnionType
来做这件事情,这样即使需求变更了,需要多加几个字段,也不需要后端去修改,只需要前端在请求中多拿几个字段就行了。
避免 N +1 问题
之前我们说了一个设定,那就是 Node 经常在使用中和 Model 简单的绑定,然后框架内部会帮你去调用查询,但是这样在获取列表的过程中,就会调用 N 次这样的查询,特别是当列表长度长,查询复杂度高,Node 节点多,一次查询就可能会导致数据库直接 Boom,这种时候我们必须用一些 loader 之类的手段,会一口气查询出所有的内容。
最后
祝大家新年快乐,这篇文章又是一如既往的拖更到最后忘了自己要写什么 -v-
植入部分
如果您觉得文章不错,可以通过赞助支持我。
如果您不希望打赏,也可以通过关闭广告屏蔽插件的形式帮助网站运作。
在使用graphql对后端数据进行自由组装时其实面临的最大问题倒不是gql的具体使用,而是相关的鉴权逻辑
按照传统的思路,需要给每一类组装都添加对应的鉴权逻辑,使得实际使用中并没有获得比传统的restful api模式更好的结果
但是如果改变思路采用对每一个数据源(model)都做单独的权限校验(封装在对应的query或者mutation中),那么前端在进行调用时就可以不用按类别去实现对应的鉴权中间件。这样模式才能发挥gql的优势。。。。吧
前端实现鉴权中间件的意思是? 是指前端不用先判断是否有权限再决定是否调用某个接口吗?权限其实可以是字段级别的,精确到某一个字段,但本质上我认为 Restful 在组装数据时也可以做相同的事情。
学习了。