vlambda博客
学习文章列表

GraphQL真香入门教程

看完复联四,我整理了这份 GraphQL 入门教程,哈哈真香。。。

首先有请阿爸镇贴!哈哈哈,需要高清原图的小伙伴可以 [点我下载 阿爸无敌] (http://images.pingan8787.com/iron_man1.jpg) 。

GraphQL真香入门教程

下面开始本文内容:

一、GraphQL介绍

GraphQL 是 Facebook 开发的一种 API 的查询语言,与 2015 年公开发布,是 REST API 的替代品。

GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。

官网: http://graphql.org/
中文网: http://graphql.cn/

1. 特点

  • 请求你所要的数据,不多不少;

如: hero 中有 name, age, sex 等,可以只取得需要的字段。

  • 获取多个资源,只用一个请求;

典型的 REST API 请求多个资源时得载入多个 URL,而 GraphQL 可以通过一次请求就获取你应用所需的所有数据。这样也能保证在较慢的移动网络连接下,使用 GraphQL 的应用也能表现得足够迅速。

  • 描述所有可能类型的系统。便于维护,根据需求平滑演进,添加或隐藏字段;

GraphQL 使用类型来保证应用只请求可能的数据,还提供了清晰的辅助性错误信息。应用可以使用类型,而避免编写手动解析代码。

2. 简单案例

这里先看下简单案例,体验下 GraphQL 的神奇之处(后面详细介绍)。
我们这样定义查询语句:

 
   
   
 
  1. query {

  2. hero

  3. }

然后得到的就是我们所要查询的 hero 字段:

 
   
   
 
  1. {

  2. "data": {

  3. "hero": "I'm iron man"

  4. }

  5. }

这样用起来,是不是更舒服呢?

二、GraphQL与restful对比

1. restful介绍

全称: RepresentationalStateTransfer 表属性状态转移。
本质上就是定义 uri ,通过 API 接口来取得资源。通用系统架构,不受语言限制。

例子: 饿了吗接口。
如:接口 restapi/shopping/v3/restaurants?latitude=13 就是个典型的 restful 接口,定义资源 + 查询条件。

2. 与 GraphQL 比较


  • restful 一个接口只能返回一个资源, GraphQL一次可以获取多个资源。



  • restful 用不同 url 来区分资源, GraphQL 用类型区分资源。


三、使用express构建基本helloworld

1. 简单案例

首先创建一个文件夹 demo ,并初始化一个 package.json,安装 express / graphql / express-graphql 依赖包:

 
   
   
 
  1. npm init -y

  2. npm install express graphql express-graphql -S

新建一个 hello.js,引入文件:

 
   
   
 
  1. const express = require('express')

  2. const { buildSchema } = require('graphql')

  3. const graphqlHTTP = require('express-graphql')

创建一个 schema 来定义查询语句和类型, buildSchema() 方法需要传入的参数是字符串类型,如下面的 hero 查询字段,后面的 String 类型表示字段返回的数据类型:

 
   
   
 
  1. const schema = buildSchema(`

  2. type Query {

  3. hero: String

  4. }

  5. `)

创建一个 root 处理器,处理对应的查询,这里的 hello 处理器对应的是 schema 中的 hero 字段查询的处理,这里直接返回 I'm iron man 的结果:

 
   
   
 
  1. const root = {

  2. hero: () => {

  3. return "I'm iron man"

  4. }

  5. }

当然,处理器中也可以是其他复杂操作,后面会介绍。

然后实例化 express ,并且将路由转发给 graphqlHTTP 处理:

 
   
   
 
  1. const app = express()

  2. app.use('/graphql', graphqlHTTP({

  3. schema,

  4. rootValue: root,

  5. graphiql: true

  6. }))

  7. app.listen(3000)

graphqlHTTP 中的三个参数介绍:

  • schema:定义的查询语句和类型

  • rootValue:处理对应查询的处理器

  • graphiql:是否开启调试窗口,开发阶段开启,生产阶段关闭

GraphQL真香入门教程

另外我们可以在 graphiql 界面右侧打开 Docs 查看我们定义的所有字段和描述信息。

GraphQL真香入门教程

GraphQL真香入门教程

最终代码:

 
   
   
 
  1. const express = require('express')

  2. const { buildSchema } = require('graphql')

  3. const graphqlHTTP = require('express-graphql')


  4. // 构建schema,这里定义查询的语句和类型

  5. const schema = buildSchema(`

  6. type Query {

  7. hero: String

  8. }

  9. `)


  10. // 定义查询所对应的 resolver,也就是查询对应的处理器

  11. const root = {

  12. hero: () => {

  13. return "I'm iron man"

  14. }

  15. }


  16. const app = express()


  17. // 将路由转发给 graphqlHTTP 处理

  18. app.use('/graphql', graphqlHTTP({

  19. schema: schema,

  20. rootValue: root,

  21. graphiql: true

  22. }))


  23. app.listen(3000)

2. 自定义类型查询

我们前面的查询中,已经将 hero 字段定义为 String 类型,但是常常开发中,我们又会碰到字段是多个类型,即字段也能指代对象类型(Object),比如一个 user 字段会有 nameage 等字段,而 name 返回字符串类型, age 返回数值类型。

这时候,我们可以对这个对象的字段进行次级选择(sub-selection)。GraphQL 查询能够遍历相关对象及其字段,使得客户端可以一次请求查询大量相关数据,而不像传统 REST 架构中那样需要多次往返查询。

我们可以新建一个查询类型来定义 user 字段返回的类型:

 
   
   
 
  1. const schema = buildSchema(`

  2. type User {

  3. # 查询可以有备注!

  4. name: String

  5. age: Int

  6. }

  7. type Query {

  8. hero: String

  9. user: User

  10. }

  11. `)

在处理器中我们也要加上:

 
   
   
 
  1. const root = {

  2. hero: () => {

  3. return "I'm iron man"

  4. },

  5. user: () => {

  6. return {

  7. name: 'leo',

  8. age: 18

  9. }

  10. }

  11. }

这边 Int/String 参数类型的问题,下一章介绍

四、参数类型和参数传递

1. 基本参数类型

String, Int, Float, BooleanID,这些基本参数类型可以在 schema 声明中直接使用。

  • Int:有符号 32 位整数。

  • Float:有符号双精度浮点值。

  • String: UTF8 字符序列。

  • Boolean: true 或者 false

  • ID: ID 标量类型表示一个唯一标识符,通常用以重新获取对象或者作为缓存中的键。 ID 类型使用和 String 一样的方式序列化;然而将其定义为 ID 意味着并不需要人类可读型。

另外,我们可以使用 [类型] 来表示一类数组,如:

  • [Int] 表示整型数组;

  • [String] 表示字符串型数组;

2. 参数传递

使用方式和 JS 参数传递一样,小括号内定义形参,但是参数需要定义类型。

使用 ! 代表参数不能为空。

下面案例:参数 teamNameString 类型,必须传递,而 number 参数也是 Int 类型,但是是非必须传递,最后输出的结果也是 String 类型。

 
   
   
 
  1. type Query {

  2. getHero(teamName: String!, number: Int): [String]

  3. }

下面一个案例:

 
   
   
 
  1. //...省略其他

  2. const schema = buildSchema(`

  3. type Query {

  4. getHero(teamName: String!): [String]

  5. }

  6. `)


  7. const root = {

  8. getHero: ({teamName}) => {

  9. // 这里的操作 实际开发中常常用在请求数据库

  10. const hero = {

  11. '三国': ['张飞', '刘备', '关羽'],

  12. '复仇者联盟': ['钢铁侠', '美国队长', '绿巨人']

  13. }

  14. return hero[teamName]

  15. }

  16. }

  17. //...省略其他

这时候我们在 GraphiQL 上输入查询,就会得到 复仇者联盟 的英雄数据了。

 
   
   
 
  1. // 查询

  2. query {

  3. getHero(teamName:"复仇者联盟")

  4. }


  5. // 结果

  6. {

  7. "data": {

  8. "getHero": [

  9. "钢铁侠",

  10. "美国队长",

  11. "绿巨人"

  12. ]

  13. }

  14. }

3. 自定义返回类型

在实际开发中,我们返回的数据类型可能是一个对象,对象中可能既有 Int 类型的属性,也有 String 类型的值,等等,这里我们可以使用 自定义返回类型 来处理:

 
   
   
 
  1. //...省略其他

  2. const schema = buildSchema(`

  3. type Hero {

  4. name: String

  5. age: Int

  6. doSomething(thing: String): String

  7. }

  8. type Query {

  9. getSuperHero(heroName: String!): Hero

  10. }

  11. `)

  12. const root = {

  13. getSuperHero: ({heroName}) => {

  14. // 这里的操作 实际开发中常常用在请求数据库

  15. const name = heroName

  16. const age = 18

  17. const doSomething = ({thing}) => {

  18. return `I'm ${name}, I'm ${thing} now`

  19. }

  20. return { name, age, doSomething }

  21. }

  22. }

  23. //...省略其他

这里指定了 getSuperHero 字段的返回类型是 Hero 类型,随后在上面定义了 Hero

其中 Hero 类型中的 doSomething也是可以传递指定类型参数,并且指定返回类型。

下面看下输出情况:

 
   
   
 
  1. // 查询

  2. query {

  3. getSuperHero(heroName:"IronMan") {

  4. name

  5. age

  6. doSomething

  7. }

  8. }


  9. // 结果

  10. {

  11. "data": {

  12. "getSuperHero": {

  13. "name": "IronMan",

  14. "age": 46,

  15. "doSomething": "I'm IronMan, I'm undefined now"

  16. }

  17. }

  18. }

这里也可以给 doSomething 传递参数,就会获取到不同结果:

 
   
   
 
  1. // 查询

  2. query {

  3. getSuperHero(heroName:"IronMan") {

  4. name

  5. age

  6. doSomething(thing:"watching TV")

  7. }

  8. }


  9. // 结果

  10. {

  11. "data": {

  12. "getSuperHero": {

  13. "name": "IronMan",

  14. "age": 46,

  15. "doSomething": "I'm IronMan, I'm watching TV now"

  16. }

  17. }

  18. }

五、GraphQL客户端

这一节我们学习如何在客户端中访问 graphql 的接口。

1. 后端定义接口

我们先在后端将接口开发完成,这里跟前面差不多,但需要多一步,使用 express 向外暴露一个文件夹,供用户访问静态资源文件:

这里直接使用前一节的代码啦~

 
   
   
 
  1. // index.js 开发 graphql 接口

  2. //...省略其他

  3. const schema = buildSchema(`

  4. type Hero {

  5. name: String

  6. age: Int

  7. doSomething(thing: String): String

  8. }

  9. type Query {

  10. getSuperHero(heroName: String!): Hero

  11. }

  12. `)

  13. const root = {

  14. getSuperHero: ({heroName}) => {

  15. // 这里的操作 实际开发中常常用在请求数据库

  16. const name = heroName

  17. const age = 46

  18. const doSomething = ({thing}) => {

  19. return `I'm ${name}, I'm ${thing} now`

  20. }

  21. return { name, age, doSomething }

  22. }

  23. }

  24. const app = express()

  25. app.use('/graphql', graphqlHTTP({

  26. schema, rootValue: root, graphiql: true

  27. }))

  28. // 公开文件夹 使用户访问静态资源

  29. app.use(express.static('public'))

  30. app.listen(3000)

这样我们就给前端页面提供一个可以访问静态资源的功能。

这里还需要在根目录创建一个 public 文件夹,并在文件夹中添加 index.html 文件,此时的目录结构:

 
   
   
 
  1. |-node_modules

  2. |-public

  3. |---index.html

  4. |-index.js

  5. |-package.json

2. 前端页面请求

然后给 index.html 添加按钮和事件绑定:

这里的变量 query 是个字符串类型,定义查询条件,在条件 GetSuperHero 中的参数,需要用 $ 符号来标识,并在实际查询 getSuperHero 中,作为参数的参数类型设置进来。

然后定义变量 variables ,指定属性的值,之后通过 fetch 发起请求:

 
   
   
 
  1. <button onclick="getData()">获取数据</button>

  2. <script>

  3. function getData(){

  4. const query = `

  5. query GetSuperHero($heroName: String, $thing: String){

  6. getSuperHero(heroName: $heroName){

  7. name

  8. age

  9. doSomething(thing: $thing)

  10. }

  11. }

  12. `

  13. // 如果不需要其他参数 至少要传一个参数 否则会报错

  14. // const query = `

  15. // query GetSuperHero($heroName: String){

  16. // getSuperHero(heroName: $heroName){

  17. // name

  18. // }

  19. // }

  20. // `

  21. const variables = {heroName: '钢铁侠', thing: 'watching TV'}


  22. fetch('./graphql', {

  23. method: 'POST',

  24. headers: {

  25. 'Content-Type': 'application/json',

  26. 'Accept': 'application/json'

  27. },

  28. body: JSON.stringify({

  29. query, variables

  30. })

  31. })

  32. .then(res => res.json())

  33. .then(json => {

  34. console.log(json)

  35. })

  36. }

  37. </script>

当我们写完以后,点击 获取数据 就会在控制台打印下面的数据:

 
   
   
 
  1. {

  2. "data":{

  3. "getSuperHero":{

  4. "name":"钢铁侠",

  5. "age":46,

  6. "doSomething": "I'm 钢铁侠, I'm watching TV now"

  7. }

  8. }

  9. }

3. 注意点

  • 请求中的 query 参数需要对照好有 $ 符号的变量。

查询语句 queryGetSuperHero($heroName:String) 里参数 $heroName 中的 heroName
查询语句 getSuperHero(heroName:$heroName) 里类型 $heroName 中的 heroName
变量 variables 中的 heroName 属性;

这三个名称需要一样。

  • 请求中需要将数据序列化操作。

 
   
   
 
  1. body: JSON.stringify({ query, variables })

六、使用Mutations修改数据

1. Mutation 使用

根据前面的学习,我们知道,要做查询操作,需要使用 Query 来声明:

 
   
   
 
  1. type Query {

  2. queryHero(heroName: String): String

  3. }

当我们要做修改操作,需要用到的是 Mutation :

 
   
   
 
  1. type Mutation {

  2. createHero(heroName: String): String

  3. }

如果 Mutation 中字段的形参是自定义类型,则类型需要用 input 标识:

 
   
   
 
  1. const schema = buildSchema(`

  2. # 输入类型 input 标识

  3. input HeroInput {

  4. name: String

  5. age: Int

  6. }

  7. # 查询类型

  8. type Hero {

  9. name: String

  10. age: Int

  11. }

  12. type Mutation {

  13. createHero(heroName: String): Hero

  14. updateHero(heroName: String, hero: HeroInput): Hero

  15. }

  16. `)

注意下:这里需要至少定义一个 Query 不然 GraphiQL 会不显示查询:

 
   
   
 
  1. type Query {

  2. hero: [Hero]

  3. }

2. Mutation 使用案例

先创建一个 schema ,内容为上一步【1. Mutation 使用】中定义的内容,这里不重复写。
然后模拟创建一个本地数据库 localDb, 用于模拟存放添加的超级英雄数据:

 
   
   
 
  1. const localDb = {}

接下来声明 root 实现 schema 中的字段方法:

 
   
   
 
  1. const root = {

  2. hero() {

  3. // 这里需要转成数组 因为前面定义了返回值是 [Hero] 类型

  4. let arr = []

  5. for(const key in localDb){

  6. arr.push(localDb[key])

  7. }

  8. return arr

  9. },

  10. createHero({ input }) {

  11. // 相当于数据库的添加操作

  12. localDb[input.name] = input

  13. return localDb[input.name]

  14. },

  15. updateHero({ id, input }) {

  16. // 相当于数据库的更新操作

  17. const update = Object.assign({}, localDb[id], input)

  18. localDb[id] = update

  19. return update

  20. }

  21. }

最后配置 graphqlHTTP 方法和启动服务器,这里就不多重复咯。

最终代码:

 
   
   
 
  1. //...省略其他

  2. const schema = buildSchema(`

  3. # 输入类型 input 标识

  4. input HeroInput {

  5. name: String

  6. age: Int

  7. }

  8. # 查询类型

  9. type Hero {

  10. name: String

  11. age: Int

  12. }

  13. type Mutation {

  14. createHero(input: HeroInput): Hero

  15. updateHero(id: ID!, input: HeroInput): Hero

  16. }

  17. # 需要至少定义一个 Query 不要GraphiQL会不显示查询

  18. type Query {

  19. hero: [Hero]

  20. }

  21. `)


  22. const localDb = {}


  23. const root = {

  24. hero() {

  25. // 这里需要转成数组 因为前面定义了返回值是 [Hero] 类型

  26. let arr = []

  27. for(const key in localDb){

  28. arr.push(localDb[key])

  29. }

  30. return arr

  31. },

  32. createHero({ input }) {

  33. // 相当于数据库的添加操作

  34. localDb[input.name] = input

  35. return localDb[input.name]

  36. },

  37. updateHero({ id, input }) {

  38. // 相当于数据库的更新操作

  39. const update = Object.assign({}, localDb[id], input)

  40. localDb[id] = update

  41. return update

  42. }

  43. }

  44. //...省略其他

现在我们可以启动服务器,在 GraphiQL 上测试下效果了。

我们是使用 mutationcreateHero 字段添加两条数据:

 
   
   
 
  1. mutation {

  2. createHero(input: {

  3. name: "钢铁侠"

  4. age: 40

  5. }){

  6. name

  7. age

  8. }

  9. }


 
   
   
 
  1. mutation {

  2. createHero(input: {

  3. name: "美国队长"

  4. age: 41

  5. }){

  6. name

  7. age

  8. }

  9. }

然后使用 queryhero 字段查询添加的结果:

 
   
   
 
  1. query {

  2. hero {

  3. name

  4. age

  5. }

  6. }

这样我们就获取到刚才的添加结果:

 
   
   
 
  1. {

  2. "data": {

  3. "hero": [

  4. {

  5. "name": "钢铁侠",

  6. "age": 40

  7. },

  8. {

  9. "name": "美国队长",

  10. "age": 41

  11. }

  12. ]

  13. }

  14. }

然后我们开始更新数据,使用 mutationupdateHero 字段将 美国队长 的 age 值修改为 18:

 
   
   
 
  1. mutation {

  2. updateHero(id: "美国队长", input: {

  3. age: 18

  4. }){

  5. age

  6. }

  7. }

再使用 queryhero 字段查询下新的数据,会发现 美国队长 的 age 值已经更新为 18:

 
   
   
 
  1. {

  2. "data": {

  3. "hero": [

  4. {

  5. "name": "钢铁侠",

  6. "age": 40

  7. },

  8. {

  9. "name": "美国队长",

  10. "age": 18

  11. }

  12. ]

  13. }

  14. }

七、认证和中间件

我们知道,修改数据的接口不能让所有人随意访问,所以需要添加权限认证,让有权限的人才可以访问。
express 中,可以很简单的使用中间件来将请求进行拦截,将没有权限的请求过滤并返回错误提示。

中间件实际上是一个函数,在接口执行之前,先拦截请求,再决定我们是否接着往下走,还是返回错误提示。

这在【六、使用Mutations修改数据】的最终代码上,在添加这个中间件:

 
   
   
 
  1. //... 省略其他

  2. const app = express()

  3. const middleWare = (req, res, next) => {

  4. // 这里是简单模拟权限

  5. // 实际开发中 更多的是和后端进行 token 交换来判断权限

  6. if(req.url.indexOf('/graphql') !== -1 && req.headers.cookie.indexOf('auth') === -1){

  7. // 向客户端返回一个错误信息

  8. res.send(JSON.stringify({

  9. err: '暂无权限'

  10. }))

  11. return

  12. }

  13. next() // 正常下一步

  14. }

  15. // 注册中间件

  16. app.use(middleWare)


  17. //... 省略其他

这里的权限判断,只是简单模拟,实际开发中,更多的是和后端进行 token 交换来判断权限(或者其他形式)。
我们重启服务器,打开 http://localhost:3000/graphql ,发现页面提示错误了,因为 cookies 中没有含有 auth 字符串。

如果这里提示 TypeError:Cannotreadproperty'indexOf'ofundefined ,可以先不用管,因为浏览器中没有 cookies 的原因,其实前面的权限判断逻辑需要根据具体业务场景判断。

为了方便测试,我们在 chrome 浏览器控制台的 application 下,手动设置一个含有 auth 字符串的一个 cookies ,只是测试使用哦。

设置完成后,我们就能正常进入页面。

八、ConstructingTypes

在前面的介绍中,我们要创建一个 schema 都是使用 buildSchema 方法来定义,但我们也可以使用另外一种定义方式。
就是这里要学习使用的构造函数 graphql.GraphQLObjectType 定义,它有这么几个优点和缺点:

  • 优点:报错提醒更直观,结构更清晰,更便于维护。

  • 缺点:代码量上升。

1. 定义type(类型)

这里先将前面定义的 Hero 类型进行改造:

 
   
   
 
  1. const graphql = require('graphql') // 需要引入

  2. const HeroType = new graphql.GraphQLObjectType({

  3. name: 'Hero',

  4. fields: {

  5. name:{ type: graphql.GraphQLString },

  6. age:{ type: graphql.GraphQLInt },

  7. }

  8. })

GraphQL真香入门教程

两者区别在于:

区别 buildSchema graphql.GraphQLObjectType
参数类型 字符串 对象
类名 跟在 type 字符后面,这里是 typeHero 在参数对象的 name 属性上
属性定义 定义在类型后,键值对形式 定义在参数对象 fields 属性中,值为对象,每个属性名为键名,值也是对象,其中 type属性的值为 graphql 中的属性,下面会补充

补充:
fields 属性中的子属性的类型通常有:

  • graphql.GraphQLString

  • graphql.GraphQLInt

  • graphql.GraphQLBoolean ....

即在 GraphQL后面跟上基本类型名称。

2. 定义query(查询)

定义查询的时候,跟之前类似,可以参照下面对比图理解,这里比较不同的是,多了个 resolve 的方法,这个方法是用来执行处理查询的逻辑,其实就是之前的 root 中的方法。

 
   
   
 
  1. const QueryType = new graphql.GraphQLObjectType({

  2. name: 'Query',

  3. fields: {

  4. // 一个个查询方法

  5. getSuperHero: {

  6. type: HeroType,

  7. args: {

  8. heroName: { type: graphql.GraphQLString }

  9. },

  10. // 方法实现 查询的处理函数

  11. resolve: function(_, { heroName }){

  12. const name = heroName

  13. const age = 18

  14. return { name, age }

  15. }

  16. }

  17. }

  18. })

GraphQL真香入门教程

3. 创建 schema

创建的时候只需实例化并且将参数传入即可:

 
   
   
 
  1. // step3 构造 schema

  2. const schema = new graphql.GraphQLSchema({ query: QueryType})

最后使用也是和前面一样:

 
   
   
 
  1. const app = express()


  2. app.use('/graphql', graphqlHTTP({

  3. schema,

  4. graphiql: true

  5. }))

  6. app.listen(3000)

九、与数据库结合实战

我们试着使用前面所学的内容,开发一个简单的实践项目:
通过 GraphiQL 页面,往 Mongodb 中插入和更新数据,主要用到【六、使用Mutations修改数据】章节的操作。

1. 搭建并启动本地 Mongodb 数据库

首先我们可以到 Mongodb 官网 选择对应平台和版本下载安装。

下载安装步骤,可以参考 mongoDB下载、安装和配置,这里就不多介绍哟~~

安装完成后,我们打开两个终端,分别执行下面两行命令:

 
   
   
 
  1. // 终端1 启动数据库

  2. mongod --dbpath c:\leo\app\mongodb\data\db


  3. // 终端2 进入数据库命令行操作模式

  4. mongo

2. 连接数据库,创建 Schema 和 Model

首先我们新建一个文件 db.js ,并 npm install mongoose 安装 mongoose ,然后写入下面代码,实现连接数据库:

 
   
   
 
  1. const express = require('express')

  2. const { buildSchema } = require('graphql')

  3. const graphqlHTTP = require('express-graphql')

  4. const mongoose = require('mongoose')


  5. const DB_PATH = 'mongodb://127.0.0.1:27017/hero_table'

  6. const connect = () => {

  7. // 连接数据库

  8. mongoose.connect(DB_PATH)

  9. // 连接断开

  10. mongoose.connection.on('disconnected', () => {

  11. mongoose.connect(DB_PATH)

  12. })

  13. // 连接失败

  14. mongoose.connection.on('error', err => {

  15. console.error(err)

  16. })

  17. // 连接成功

  18. mongoose.connection.on('connected', async () => {

  19. console.log('Connected to MongoDB connected', DB_PATH)

  20. })

  21. }

  22. connect()

然后创建 SchemaModel

 
   
   
 
  1. let HeroSchema = new mongoose.Schema({

  2. name: String,

  3. age: Number

  4. })

  5. let HeroModel = mongoose.model('hero',HeroSchema, 'hero_table')

3. 声明查询语句

这一步,还是先使用【六、使用Mutations修改数据】章节的操作逻辑,也就是先用字符串创建查询,而不使用 GraphQLObjectType 创建:

 
   
   
 
  1. const schema = buildSchema(`

  2. # 输入类型 input 标识

  3. input HeroInput {

  4. name: String

  5. age: Int

  6. }

  7. # 查询类型

  8. type Hero {

  9. name: String

  10. age: Int

  11. }

  12. type Mutation {

  13. createHero(input: HeroInput): Hero

  14. updateHero(hero: String!, input: HeroInput): Hero

  15. }

  16. # 需要至少定义一个 Query 不要GraphiQL会不显示查询

  17. type Query {

  18. hero: [Hero]

  19. }

  20. `)

这边案例有稍作修改

4. 实现添加数据和更新数据的逻辑

这边处理添加数据和更新数据的逻辑,就要修改之前声明的 root 的操作内容了:

 
   
   
 
  1. const root = {

  2. hero() {

  3. return new Promise( (resolve, reject) => {

  4. HeroModel.find((err, res) => {

  5. if(err) {

  6. reject(err)

  7. return

  8. }

  9. resolve(res)

  10. })

  11. })

  12. },

  13. createHero({ input }) {

  14. // 实例化一个Model

  15. const { name, age } = input

  16. const params = new HeroModel({ name, age })

  17. return new Promise( (resolve, reject) => {

  18. params.save((err, res) => {

  19. if(err) {

  20. reject(err)

  21. return

  22. }

  23. resolve(res)

  24. })

  25. })

  26. },

  27. updateHero({ hero, input }) {

  28. const { age } = input

  29. return new Promise ((resolve, reject) => {

  30. HeroModel.update({name: hero}, {age}, (err, res) => {

  31. if(err) {

  32. reject(err)

  33. return

  34. }

  35. resolve(res)

  36. })

  37. })

  38. }

  39. }

5. 模拟测试

最后我们在 GraphiQL 页面上模拟测试一下,首先添加两个英雄,钢铁侠和美国队长,并设置他们的 age/name 属性:

 
   
   
 
  1. mutation {

  2. createHero(input: {

  3. name: "钢铁侠"

  4. age: 40

  5. }){

  6. name

  7. age

  8. }

  9. }

 
   
   
 
  1. mutation {

  2. createHero(input: {

  3. name: "美国队长"

  4. age: 20

  5. }){

  6. name

  7. age

  8. }

  9. }

页面和接口没有报错,说明我们添加成功,数据库中也有这两条数据了:

GraphQL真香入门教程

在测试下查询:

 
   
   
 
  1. query {

  2. hero {

  3. name

  4. age

  5. }

  6. }

查询也正常,接下来测试下更新,将美国队长的 age 修改为 60:

 
   
   
 
  1. mutation {

  2. updateHero(hero: "美国队长", input: {

  3. age: 60

  4. }){

  5. age

  6. }

  7. }

到这一步,我们也算是将这个练习做完了。

总结


  • GraphQL 是一种 API 的查询语言,是 REST API 的替代品。



  • GraphQL 可以使用一个请求,获取所有想要的数据。



  • 创建查询的方式有两种:使用 buildSchema 或者 GraphQLObjectType



  • 查询操作用 Query,修改操作用 Mutations



  • 查询类型用 type ,输入类型用 input


其实 GraphQL 还是很简单好用的呢~~~


本文首发在 [pingan8787个人博客] (http://www.pingan8787.com),如需转载请保留个人介绍