GraphQL 的查询和变更

本文在 node.js 的环境 ( 详见初始 GraphQL )搭建好的条件下,从零开始构建一套基础的数据操作功能。现以 Oracle 经典入门系列 soctt 数据作为基础数据,实现一套接功能。

1|0定义 Schema

​ GraphQL 思想是以数据为中心的,定义 Schema 自然成为了开发的核心。定义 Schema 即是定义数据模型,需要根据业务制定,客户端和服务端共同维护。

/** schema **/ type Dept { deptno: ID! dname: String loc: String } type Emp { empno: ID! ename: String job: String mgr: Int hiredate: String sal: Int comm: String deptno: String! } type Bonus { ename: String job: String sal: Int comm: String } type Salgrade { grade: ID! losal: Int hisal: Int }

2|0设置相关接口的操作 Schema 和 Resolver

​ Schema 中大部分的类型都是普通对象类型,但是一个 schema 内有两个特殊类型。

schema { query: Query mutation: Mutation }

​ 每一个 GraphQL 服务都有一个 query 类型,可能有一个 mutation 类型。这两个类型和常规对象类型无差,但是它们之所以特殊,是因为它们定义了每一个 GraphQL 查询的入口。

/** schema **/ type Query{ getAllDepts: [Dept] getAllEmps: [Emp] getAllBonus: [Bonus] getAllSalgrades: [Salgrade] }
/** resolver **/ const { bonus, dept, emp, salgrade } = require('./fakeDB/index.js') const resolves = { getAllDepts: () => dept, getAllEmps: () => emp, getAllBonus: () => bonus, getAllSalgrades: () => salgrade }
/** client **/ query{ getAllDepts{ deptno dname loc } getAllEmps{ empno ename job mgr hiredate sal comm deptno } getAllBonus{ ename job sal comm } getAllSalgrades{ grade losal hisal } }
/** response **/ { "data": { "getAllDepts": [ { "deptno": "10", "dname": "ACCOUNTING", "loc": "NEW YORK" }, { "deptno": "20", "dname": "RESEARCH", "loc": "DALLAS" }, { "deptno": "30", "dname": "SALES", "loc": "CHICAGO" }, { "deptno": "40", "dname": "OPERATIONS", "loc": "BOSTON" } ], "getAllEmps": [ { "empno": "7369", "ename": "SMITH", "job": "CLERK", "mgr": "7902", "hiredate": "17-12-1980", "sal": 800, "comm": "", "deptno": "20" }, { "empno": "7499", "ename": "ALLEN", "job": "SALESMAN", "mgr": "7698", "hiredate": "20-2-1981", "sal": 1600, "comm": "300", "deptno": "30" }, { "empno": "7521", "ename": "WARD", "job": "SALESMAN", "mgr": "7698", "hiredate": "22-2-1981", "sal": 1250, "comm": "500", "deptno": "30" }, { "empno": "7566", "ename": "JONES", "job": "MANAGER", "mgr": "7839", "hiredate": "2-4-1981", "sal": 2975, "comm": "", "deptno": "20" }, { "empno": "7654", "ename": "MARTIN", "job": "SALESMAN", "mgr": "7698", "hiredate": "28-9-1981", "sal": 1250, "comm": "1400", "deptno": "30" }, { "empno": "7698", "ename": "BLAKE", "job": "MANAGER", "mgr": "7839", "hiredate": "1-5-1981", "sal": 2850, "comm": "", "deptno": "30" }, { "empno": "7782", "ename": "CLARK", "job": "MANAGER", "mgr": "7839", "hiredate": "9-6-1981", "sal": 2450, "comm": "", "deptno": "10" }, { "empno": "7788", "ename": "SCOTT", "job": "ANALYST", "mgr": "7566", "hiredate": "13-7-87", "sal": 3000, "comm": "", "deptno": "20" }, { "empno": "7839", "ename": "KING", "job": "PRESIDENT", "mgr": "", "hiredate": "17-11-1981", "sal": 5000, "comm": "", "deptno": "10" }, { "empno": "7844", "ename": "TURNER", "job": "SALESMAN", "mgr": "7698", "hiredate": "8-9-1981", "sal": 1500, "comm": "0", "deptno": "30" }, { "empno": "7876", "ename": "ADAMS", "job": "CLERK", "mgr": "7788", "hiredate": "13-7-87", "sal": 1100, "comm": "", "deptno": "20" }, { "empno": "7900", "ename": "JAMES", "job": "CLERK", "mgr": "7698", "hiredate": "3-12-1981", "sal": 950, "comm": "", "deptno": "30" }, { "empno": "7902", "ename": "FORD", "job": "ANALYST", "mgr": "7566", "hiredate": "3-12-1981", "sal": 3000, "comm": "", "deptno": "20" }, { "empno": "7934", "ename": "MILLER", "job": "CLERK", "mgr": "7782", "hiredate": "23-1-1982", "sal": 1300, "comm": "", "deptno": "10" } ], "getAllBonus": [], "getAllSalgrades": [ { "grade": "1", "losal": 700, "hisal": 1200 }, { "grade": "2", "losal": 1201, "hisal": 1400 }, { "grade": "3", "losal": 1401, "hisal": 2000 }, { "grade": "4", "losal": 2001, "hisal": 3000 }, { "grade": "5", "losal": 3001, "hisal": 9999 } ] } }

3|0传参

​ 以上演示了基础的查询与数据,但实际开发中不可能只有全量查询,更多的情况下是条件查询。

3|1参数(Arguments)

​ 在类似 REST 的系统中,你只能传递一组简单参数 -- 请求中的 query 参数和 URL 段。但是在 GraphQL 中,每一个字段和嵌套对象都能有自己的一组参数,从而使得 GraphQL 可以完美替代多次 API 获取请求。甚至你也可以给 标量(scalar)字段传递参数,用于实现服务端的一次转换,而不用每个客户端分别转换。

/** schema **/ type Query{ getEmpByEmpno(empno : String) : Emp }
/** resolver **/ const resolves = { getEmpByEmpno: (args) => { return emp.find((item) => { return args.empno == item.empno }) } }
/** client **/ query{ getEmpByEmpno(empno:"7934"){ ename job deptno } }
/** response **/ { "data": { "getEmpByEmpno": { "ename": "MILLER", "job": "CLERK", "deptno": "10" } } }

值的注意的是: 在 Schema 中,所有的参数到达 Resolver 后,将合并成一个Object 类型的参数,这与我们习惯中的函数形参不太一样。

3|2变量(Variables)

​ 上例中参数 empno 是直接在客户端直接传值。若需要动态传值时,其中一种选择就是在客户端动态的去拼接 query ,但这显然不是一个好方法。GraphQL 拥有一级方法将动态值提取到查询之外,然后作为分离的字典传进去。这些动态值即称为变量。使用变量之前,我们得做三件事:

1. 使用 `$variableName` 替代查询中的静态值。 2. 声明 `$variableName` 为查询接受的变量之一。 3.`variableName: value` 通过传输专用(通常是 JSON)的分离的变量字典中。
/** client **/ query ($empno: String){ getEmpByEmpno(empno:$empno){ ename job deptno } }
/** client - query variables **/ { "empno": "7934" }
/** response **/ { "data": { "getEmpByEmpno": { "ename": "MILLER", "job": "CLERK", "deptno": "10" } } }

3|3聚合查询

​ 在同一个上下文中,GraphQL 一次性请求可以获取所有所需的字段。

/** resolver **/ const resolves = { getDeptByDeptno: ({ deptno }) => { return dept.find((item) => { return deptno == item.deptno }) }, getEmpFromDept: ({ deptno }) => { return dept.filter((item) => { return deptno != item.deptno }) } }
/** schema **/ type Query { getDeptByDeptno (deptno : String) : Dept getEmpFromDept (deptno : String) : [Emp] }
/** client **/ query ($deptno: String){ getDeptByDeptno(deptno:$deptno){ dname } getEmpFromDept(deptno:$deptno){ ename job } }
/** client - query variables **/ { "empno": "30" }
/** response **/ { "data": { "getDeptByDeptno": { "dname": "SALES" }, "getEmpFromDept": [ { "ename": "ALLEN", "job": "SALESMAN" }, { "ename": "WARD", "job": "SALESMAN" }, { "ename": "MARTIN", "job": "SALESMAN" }, { "ename": "BLAKE", "job": "MANAGER" }, { "ename": "TURNER", "job": "SALESMAN" }, { "ename": "JAMES", "job": "CLERK" } ] } }

4|0变更

​ 变更与查询的工作方式相同,在 Mutation 类型上定义一些字段,然后这些字段将作为 mutation 根字段使用,接着就能在查询中调用。

4|1输入类型(Input Types)

​ 上例传参中传输的是标量类型,在操作变更提交的时候往往是一整个对象进行提交。GraphQL 也支持对象类型的参数,在 Schema 中定义输入类型与定义普通类型非常相似,只需将关键字改为 input 而不是 type

/** resolver **/ const { nanoid } = require('nanoid') const resolves = { increaseDept: (args) => { const deptno = nanoid() const deptInstance = { deptno, ...args.dept } return deptInstance } }
/** schema **/ input deptInput { dname: String loc: String } type Mutation { increaseDept(dept:deptInput) : Dept }
/** client **/ mutation ($dept:deptInput){ increaseDept(dept: $dept){ deptno dname loc } }
/** client - query variables **/ { "dept": { "dname": "public relations", "loc": "Boston" } }
/** response **/ { "data": { "increaseDept": { "deptno": "NUzaVwPX8rYizd3cz8jIm", "dname": "public relations", "loc": "Boston" } } }

__EOF__

本文作者Odyssey
本文链接https://www.cnblogs.com/qingzhao/p/16723526.html
关于博主:I am a good person
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   --Odyssey--  阅读(276)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示