GraphQL
本文是GraphQL的高度浓缩,若难以理解,推荐教程 前往 。
GraphQL是一个用于 API 的查询语言。他是强类型的,可以校验入参,并确定响应类型。
GraphQL有两个核心概念:GraphQL schema和GraphQL OPERATION。
GraphQL schema
![](https://docs.fireboom.io/~gitbook/image?url=https%3A%2F%2F2514536170-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FhvHBEKUXoqt1bL00q2Ra%252Fuploads%252Fgit-blob-506e96b4365070fca5695d26fafcf31ad55458c7%252Fimage.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=bad01dec&sv=1)
schema指的是graphql结构定义的集合,类似数据库schema或数据库建表语句,operation指的是从schema中取的子集,类似数据库的查询sql。
我们知道gql是一个强类型语言,它通过4个关键字:type、enum、scalar、input,定义了所有的数据结构。
其数据结构分为两种,一种叫做Scalar Type(标量类型),另一种叫做Object Type(对象类型)。
GraphQL中的内建的标量包含:String、Int、Float、Boolean、Enum,这些概念想必大家都接触过。
其中,enum表示枚举,通过enum关键字,可以定义枚举标量,例如sortorder,花括号里面的asc和desc是它的 枚举值。
GraphQL也支持通过Scalar声明新的标量,比如,这里声明了一个DateTime标量,用来表示日期格式。
总之,我们只需要记住,标量是GraphQL类型系统中最小的颗粒。\
对象类型基于标量构建,其关键字为type。每一个对象有若干字段组成,字段都有类型。例如:TODO 对象类型。它有6个Field,分别是INT类型的id,String类型的Title和Boolean类型的clompeted,xxx。
gql支持对象嵌套,因此 字段不仅可以是标量,也可以是对象,甚至可以是自循环对象。
关于类型,还有一个较重要的概念,即类型修饰符,当前的类型修饰符有两种,分别是List和Required,它们的语法分别为[Type]和Type!。前者代表数组,后者代表必填。
除了type定义对象外,input也用于定义对象,称为 参数类型。可以类比为函数入参的类型。很多编程语言没有区分对象类型和参数类型。
在这里,我们要注意下,type和input定义的对象,都支持嵌套定义,即todowhereinput类型的字段也可以是todowhereinput,可以是别的对象类型,如Intfilter。
因此,type和input,通过这种嵌套,声明了各个模型之间的内在关联(一对多、一对一或多对多)。
此外,还有3类特殊对象,query、mutation和subscription,对于传统的CRUD项目,我们只需要前两种类型就足够了,第三种是针对当前日趋流行的real-time应用提出的,这块后续会讲到。
我们一般叫这3中对象为:Query(大写)。
接下来,我们分别以REST和GraphQL的角度,以todo为数据模型,编写一系列CRUD的接口。例如上图这几个接口。
GET /api/v1/todos/
GET /api/v1/todo/:id/
POST /api/v1/todo/
DELETE /api/v1/todo/:id/
获取待做事项列表对应findmanytodo,其中findmanytodo为根字段,可以类比为一个函数名称。
我们还可以发现,GraphQL中用query和mutation两种类型代表了rest接口的众多请求类型,例如QUERY对应get请求,mutation对应POST\DELETE、patch请求。
findmanytodo既然是函数,自然有入参和出参(返回值)。其出参是数组对象。
入参也有类型,但不能用type定义的类型,而是必须要用input定义类型。例如,where参数的类型是todowhereinput类型。
其实,不只是根字段可以有入参,任意对象类型都可以有入参。这个后续我们会遇到。
至此,我们就基本掌握了如何定义gql schema。
GraphQL OPERATION
接下来,我们学习operation。
![](https://docs.fireboom.io/~gitbook/image?url=https%3A%2F%2F2514536170-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FhvHBEKUXoqt1bL00q2Ra%252Fuploads%252Fgit-blob-9629f35549ab8028ae8b12686010fab95b2f1f48%252Fimage.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=4ac21be2&sv=1)
operation指的是从schema中的Query里取的子集,如果把schema queryr中的跟字段比作函数定义,那operation就是函数调用。
operation和SCHEMA query类型一一对应,也分为3中类型,分别是query,mutation,subscription。
如该operation 调用了 findmanytodo函数,字段入参分别是take和skip。其中take为固定值10,skip为变量值,由变量$skip传入。
变量是一个新概念,是Operaiton上定义的参数,注意不是函数上的参数。变量主要用途是动态设置函数的参数。
变量支持默认值,如$skip默认值为10。此外,变量也支持用修饰符!修饰,表明该变量为必传字段。
接着我们看下operaiton的响应,例如右侧图的灰色部分,就是该operaion的响应。在gql中被称为作用于该mutation opration的选择集。
值得注意的是,一个opratinon中可以有多个函数,因此operation选择集可能会同时包含多个跟字段。
选择集大概率是对应字段的子集,例如mutation里面不仅包含del和cretat,还包括executeraw,而实际上并没有用到后者。
不仅有作用域opration上的选择集,也有作用域字段上的选择集,例如,count就是作用域deletemanytodo字段的选择集。
GraphQL SERVER
接下来,我们学习:如何使用基于GraphQL协议构建的服务。
服务启动后,本地访问地址:http://localhost:8000/graphql
,界面如下:
![](https://docs.fireboom.io/~gitbook/image?url=https%3A%2F%2F2514536170-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FhvHBEKUXoqt1bL00q2Ra%252Fuploads%252Fgit-blob-8f907156206a03f5d6c96ba164640f5ffa80ac8c%252Fimage.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=679a3a59&sv=1)
Gql服务启动后,会对外暴露gql端点,其路由一般由graphql结尾。
以GET请求访问端点时,会返回一个gql操作界面。
基于界面可以方便的构建OPERATION,并执行该OPERATION拿到响应。分别对应步骤2和3。
其中3的底层是向该Gql端点,发送POST请求。
我们以该opration为例,它有2个根字段:message和detail,其中messsage字段的类型为对象,且无入参;detail的类型为标量,有一个入参title,并通过Operation的变量$titlevar为其赋值。
当我们点击执行按钮时,其请求如下:
端点为:https://fireboom-gql.ansoncode.repl.co/graphql,请求为post类型。
请求头content-type
为json类型。
请求体有3字段:
operationName,操作名称,可以省略
query:operation的字符串
variables:operation的入参对象
该请求的响应体和operation的选择集一致,只不过会包裹在data对象里面。
GraphQL 内省
但有个问题需要回答: 该前端界面如何了解后端SCHEMA的呢?
这就要提到gql的“内省”能力,它是一种特殊的query operation 。能够根据步骤3一样的方式获取schema的结构。
之所以能实现该功能,主要得益于该端点的强类型特性。
向GraphQL端点发送下述请求,可以拿到GraphQL SCHEMA对应的JSON结构体,通过特定库可以将其转换为GraphQL SCHEMA。
参考
最后更新于