GraphQL入门实践
GraphQL是什么?
GraphQL 是一个Facebook于2012开发出来且2015开源的应用层的查询语言,你需要在后台定义一个基于GraphQL的图形模式,然后你的客户端就可以查询他们想要的数据,而不需要后台重新定义一个接口返回你需要的数据。
背景源于:在facebook内部,大量不同的app和系统共同使用着许许多多的服务api,随着业务的变化和发展,不同app对相同资源的不同使用方法最终导致需要维护的服务api数量爆炸式的增长,非常不利于维护(我们主要在restful场景上思考)。而且创建一个大而全的通用性接口又非常不利于移动端使用(流量损耗),而且后端数据的无意义聚合也对整个系统带来了很大的资源浪费。
因为不需要更改你后台,所以这种方式比 REST API 方式更好,让我们可以在不同的客户端上灵活改变数据显示。
GraphQL并不是一门程序语言或者框架,它是描述你的请求数据的一种规范,是协议而非存储,GraphQL本身并不直接提供后端存储的能力,它不绑定任何的数据库或者存储引擎,它可以利用已有的代码和技术来进行数据源管理。这意味着你可以在任何语言上实现 GraphQL
为什么要用?
GraphQL对你的API中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。 获取多个资源只用一个请求
1. 声明式。描述所有的可能类型系统 查询的结果格式由请求方(即客户端)决定而非响应方(即服务器端)决定。你不需要编写很多额外的接口来适配客户端请求
2. 减少开发文档的维护工作量,相对应的减少沟通成本
3. 强类型。每个 GraphQL 查询必须遵循其设定的类型才会被执行。
4. 请求合并 多个接口可以通过组合为一个
5. 请求你所要的数据不多不少
业务场景:
示例:
比如我需要一个商户的详细信息,可接口却会把这个商户相关的门店信息、所有人信息等其它各种各样的信息一起返回回来,如果是例如商户详情页面也就罢了,可是在商户列表这个接口下依旧返回如此之多的数据,可想而知这个列表有多大多复杂了。后端的同学说是为了兼容 web 端的需求,一个接口需要同时为多个平台提供内容,这大大增加了接口的返回内容和处理逻辑,而且需求也经常改动,所以还不如把能用到的字段全都输出出来,免得每次改需求都要前后端一起联动。
- 兼容多平台导致字段冗余
- 一个页面需要多次调用 API 聚合数据
- 需求经常改动导致接口很难为单一接口精简逻辑
不用再因为在缺乏沟通的情况下修改接口,而为系统埋下不稳定的定时炸弹。一切面向前端的接口都有强类型的 Schema 做保证,且完整类型定义因 introspection 完全对前端可见,一旦前端发送的 query 与 Schema 不符,能快速感知到产生了错误。
GraphQL 的本质是程序员想对JSON使用SQL。 —— 来自阮一峰的翻译
对比Restful:
Rest的数据是以资源为导向的,交互围绕着定位资源的路由(Route)进行,每个资源对应一个URL,由后台定义好后通过向指定URL发送请求来获取资源,客户端不能个性化的手机数据,除此之外,运行和控制多个端点是另一个难点,因为客户端经常需要从多个端点来获取数据。
而GraphQL的模型与对象模型更加类似,模型是通过图的形式组织数据。相比Rest在客户端定义响应数据的结构,GraphQL灵活地将响应数据的结构交给了客户端。这样的好处是:客户端只需要一次请求就能够获得结构复杂的数据,一个url,全部的操作都以函数为中心。
graphQL使用:
以下变量都是基于我自定义的GraphQL简单demo,参见github地址
对于客户端:
结果的顺序也是按照你输入的顺序排序的。定制化的数据,完全根据你查什么返回什么结果。这就是GraphQL被称作API查询语言的原因。
query:实现查:
1、无参数查询:
2、有参数查询:
3、嵌套查询:需要对数据进行筛选,比如限制大小时
4、同时查询多个不关联的Schema,常用于同一个前端页面中不同信息的获取
5、别名:
Mutation:实现增删改
6、
- create是一个名字,你可以随便定义,甚至不给这个名字都可以.
- createAddress是服务器已经定义好的操作,这个文档有详细信息,让你知道需要发送什么数据
合并Mutation:
7、片段(Fragment)
用片段来定义我们需要取用的字段,而不用每个对象都去重复写了。并且片段内同样可以定义变量
分片也可以嵌套分片,所以只要是服务器定义过的数据类型,你都可以写成一个个的分片,这种模式能大量减少你写重复代码的时间.
8、查询变量:
使用默认值 ($limit : Int = 1)
可以和分片一起使用,你可以增加几个变量增加使用分片:
9、使用指令(Directives)实现条件查询
当我们要通过某个条件来判断是否获取某个字段的时候可以使用指令来实现,指令包括两种:
@include(if: Boolean)表示在参数为true时包含此字段,@skip(if: Boolean)表示在参数为true时跳过此字段。
10、自省:
GraphQL是可自省的,也就是说你可以通过查询一个GraphQL知道它自己的schema细节。
你可以写一个GraphQL查询来请求GraphQL服务获取它的fields。
每个GraphQL根字段自动包含一个__Schema字段,其包含用来查询的描述自身meta信息的字段–queryType。
查询__schema以列出所有该schema中定义的类型,并获取每一个的细节:
查询__type以获取任意类型的细节:
对于服务端:
首先需要创建一个Schema实例来处理GraphQL查询。
一个标准的空的schema看起来是这样的:
import { GraphQLSchema } from 'graphql';
let schema = new GraphQLSchema({
query: queryObj : 定义查询类的接口
mutation:MutationObj:定义修改类的接口
subscription:订阅字段
});
一个基本的定义查询类对象demo:
一个基本的定义修改类对象demo:
ype可以在原有类型基础上自定义:
基本的常用类型有:
类型可以嵌套,参照Address、AddressContent
GraphQL为什么没有火起来?
来自知乎-尤雨溪的回答:16年
GraphQL存在的问题:
1、改造成本
要使用GraphQL对数据源进行管理,相当于要对整个服务端进行一次换血。你需要考虑的不仅仅是需要针对现有数据源建立一套GraphQL的类型系统,同时需要改造服务端暴露数据的方式,这对业务久远的产品无疑是一场灾难,让人望而却步。
2、查询性能
GraphQL查询的每个字段如果都有自己的resolve方法,可能导致一次查询操作对数据库跑了大量了query,数据库里一趟select+join就能完成的事情在这里看来会产生大量的数据库查询操作,虽然网络层面的请求数被优化了,但是数据库查询可能会成为性能瓶颈。但是这个其实是可以优化,就算是用rest api,同时处理多个请求的时候,也总要运行那些数据库语句的。
3、具体问题:
N+1问题:
在实现 GraphQL 服务端接口时,很容易就能写出效率极差的代码,引起 “N+1 问题”。
假设数据库中有两张表,关系如上,一个存储用户,一个根据对应User的id存储他们的成绩,
如果我们要获取一堆人(N个)的成绩时,由于用户的分数并没有保存在User表中,需要查询数据库N+1次(一次获取用户,N次获取各自的成绩)
相对于 RESTful,在 GraphQL 中更加容易引起 N+1 问题。主要是由于 GraphQL query 的逐层解析方式所引起的,没有合适的缓存和批量处理系统,每次确定字段的时候服务器都会响应一次请求。
解决:为了防止N+1问题,Facebook 为 Node.js 社区提供了 DataLoader 的实现。其原理就是,在需要查询数据库的时候将查询进行延迟,等到拿到所有的查询需求之后再一次性查询出来,同时已经加载过的数据可以直接从 DataLoader 的缓存空间中获取到。
验证问题/限流问题:
由于只有一个查询入口,不能像Restful那样针对每个接口进行单独的验证或者限流。一个简单的方法就是强制让用户传入规范命名的查询语句,通过命名来进行后续的判断,就能方便地达到我们的要求。
缓存问题:
GraphQL的一个常见问题,特别是与REST进行比较时,难以维护服务器端缓存。使用REST,可以轻松地为每个端点缓存数据,因为它确保数据的结构不会改变。
另一方面,使用GraphQL,客户端下一步要求什么不清楚,所以将缓存层放在API的后面并没有什么意义。
文中所有自定义变量都是基于一个简单的demo:后端基于Koa,前端基于React,数据库基于本地MongoDB的GraphQL的项目demo,github地址如下,欢迎大家提出意见。
https://github.com/scf-coder/koa-mongoDB-GraphQL-demo
上一篇: buuctf easyheap
下一篇: qt对话框