【CuteJavaScript】GraphQL真香入门教程 下

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
简介: 【CuteJavaScript】GraphQL真香入门教程 下

六、使用Mutations修改数据

1. Mutation 使用

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

type Query {
    queryHero(heroName: String): String
}

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

type Mutation {
    createHero(heroName: String): String
}

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

const schema = buildSchema(`
    # 输入类型 用 input 标识
    input HeroInput {
        name: String
        age: Int
    }
    # 查询类型
    type Hero {
        name: String
        age: Int
    }
    type Mutation {
        createHero(heroName: String): Hero
        updateHero(heroName: String, hero: HeroInput): Hero
    }
`)

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

type Query {
    hero: [Hero]
}

2. Mutation 使用案例

先创建一个 schema ,内容为上一步【1. Mutation 使用】中定义的内容,这里不重复写。

然后模拟创建一个本地数据库 localDb, 用于模拟存放添加的超级英雄数据:

const localDb = {}

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

const root = {
    hero() {
        // 这里需要转成数组 因为前面定义了返回值是  [Hero]  类型
        let arr = []
        for(const key in localDb){
            arr.push(localDb[key])
        }
        return arr
    },
    createHero({ input }) {
        // 相当于数据库的添加操作
        localDb[input.name] = input
        return localDb[input.name]
    },
    updateHero({ id, input }) {
        // 相当于数据库的更新操作
        const update = Object.assign({}, localDb[id], input)
        localDb[id] = update
        return update
    }
}

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

最终代码:

//...省略其他
const schema = buildSchema(`
    # 输入类型 用 input 标识
    input HeroInput {
        name: String
        age: Int
    }
    # 查询类型
    type Hero {
        name: String
        age: Int
    }
    type Mutation {
        createHero(input: HeroInput): Hero 
        updateHero(id: ID!, input: HeroInput): Hero
    }
    # 需要至少定义一个 Query 不要GraphiQL会不显示查询
    type Query {
        hero: [Hero]
    }
`)
const localDb = {}
const root = {
    hero() {
        // 这里需要转成数组 因为前面定义了返回值是  [Hero]  类型
        let arr = []
        for(const key in localDb){
            arr.push(localDb[key])
        }
        return arr
    },
    createHero({ input }) {
        // 相当于数据库的添加操作
        localDb[input.name] = input
        return localDb[input.name]
    },
    updateHero({ id, input }) {
        // 相当于数据库的更新操作
        const update = Object.assign({}, localDb[id], input)
        localDb[id] = update
        return update
    }
}
//...省略其他

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

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

mutation {
    createHero(input: {
        name: "钢铁侠"
        age: 40
    }){
        name
        age
    }
}
mutation {
    createHero(input: {
        name: "美国队长"
        age: 41
    }){
        name
        age
    }
}

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

query {
    hero {
        name
        age
    }
}

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

{
    "data": {
        "hero": [
            {
                "name": "钢铁侠",
                "age": 40
            },
            {
                "name": "美国队长",
                "age": 41
            }
        ]
  }
}

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

mutation {
    updateHero(id: "美国队长", input: {
        age: 18
    }){
        age
    }
}

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

{
    "data": {
        "hero": [
            {
                "name": "钢铁侠",
                "age": 40
            },
            {
                "name": "美国队长",
                "age": 18
            }
        ]
    }
}


七、认证和中间件

我们知道,修改数据的接口不能让所有人随意访问,所以需要添加权限认证,让有权限的人才可以访问。

express 中,可以很简单的使用中间件来将请求进行拦截,将没有权限的请求过滤并返回错误提示。

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

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

//... 省略其他
const app = express()
const middleWare = (req, res, next) => {
    // 这里是简单模拟权限
    // 实际开发中 更多的是和后端进行 token 交换来判断权限
    if(req.url.indexOf('/graphql') !== -1 && req.headers.cookie.indexOf('auth') === -1){
        // 向客户端返回一个错误信息
        res.send(JSON.stringify({
            err: '暂无权限'
        }))
        return
    }
    next() // 正常下一步
}
// 注册中间件
app.use(middleWare)
//... 省略其他

这里的权限判断,只是简单模拟,实际开发中,更多的是和后端进行 token 交换来判断权限(或者其他形式)。

我们重启服务器,打开 http://localhost:3000/graphql ,发现页面提示错误了,因为 cookies 中没有含有 auth 字符串。

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

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

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


八、ConstructingTypes

在前面的介绍中,我们要创建一个 schema 都是使用 buildSchema 方法来定义,但我们也可以使用另外一种定义方式。

就是这里要学习使用的构造函数 graphql.GraphQLObjectType 定义,它有这么几个优点和缺点:

  • 优点:报错提醒更直观,结构更清晰,更便于维护。
  • 缺点:代码量上升。

1. 定义type(类型)

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

const graphql = require('graphql') // 需要引入
const HeroType = new graphql.GraphQLObjectType({
    name: 'Hero',
    fields: {
        name:{ type: graphql.GraphQLString },
        age:{ type: graphql.GraphQLInt },
    }
})

两者区别在于:

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

补充:

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

  • graphql.GraphQLString
  • graphql.GraphQLInt
  • graphql.GraphQLBoolean ....

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

2. 定义query(查询)

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

const QueryType = new graphql.GraphQLObjectType({
    name: 'Query',
    fields: {
        // 一个个查询方法
        getSuperHero: {
            type: HeroType,
            args: {
                heroName: { type: graphql.GraphQLString }
            },
            // 方法实现 查询的处理函数
            resolve: function(_, { heroName }){
                const name = heroName
                const age = 18
                return { name, age }
            }
        }
    }
})

3. 创建 schema

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

// step3 构造 schema
const schema = new graphql.GraphQLSchema({ query: QueryType})

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

const app = express()
app.use('/graphql', graphqlHTTP({
    schema,
    graphiql: true
}))
app.listen(3000)


九、与数据库结合实战

我们试着使用前面所学的内容,开发一个简单的实践项目:

通过 GraphiQL 页面,往 Mongodb 中插入和更新数据,主要用到【六、使用Mutations修改数据】章节的操作。

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

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

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

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

// 终端1  启动数据库
mongod --dbpath c:\leo\app\mongodb\data\db
// 终端2  进入数据库命令行操作模式
mongo

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

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

const express = require('express')
const { buildSchema } = require('graphql')
const graphqlHTTP = require('express-graphql')
const mongoose = require('mongoose')
const DB_PATH = 'mongodb://127.0.0.1:27017/hero_table'
const connect = () => {
    // 连接数据库
    mongoose.connect(DB_PATH)
    // 连接断开
    mongoose.connection.on('disconnected', () => {
        mongoose.connect(DB_PATH)
    })
    // 连接失败
    mongoose.connection.on('error', err => {
        console.error(err)
    })
    // 连接成功
    mongoose.connection.on('connected', async () => {
        console.log('Connected to MongoDB connected', DB_PATH)
    })  
}
connect()

然后创建 SchemaModel

let HeroSchema = new mongoose.Schema({
    name: String,
    age: Number
})
let HeroModel = mongoose.model('hero',HeroSchema, 'hero_table')

3. 声明查询语句

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

const schema = buildSchema(`
    # 输入类型 用 input 标识
    input HeroInput {
        name: String
        age: Int
    }
    # 查询类型
    type Hero {
        name: String
        age: Int
    }
    type Mutation {
        createHero(input: HeroInput): Hero 
        updateHero(hero: String!, input: HeroInput): Hero
    }
    # 需要至少定义一个 Query 不要GraphiQL会不显示查询
    type Query {
        hero: [Hero]
    }
`)

这边案例有稍作修改

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

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

const root = {
    hero() {
        return new Promise( (resolve, reject) => {
            HeroModel.find((err, res) => {
                if(err) {
                    reject(err)
                    return
                }
                resolve(res)
            })
        })
    },
    createHero({ input }) {
        // 实例化一个Model
        const { name, age } = input
        const params = new HeroModel({ name, age })
        return new Promise( (resolve, reject) => {
            params.save((err, res) => {
                if(err) {
                    reject(err)
                    return
                }
                resolve(res)
            })
        })
    },
    updateHero({ hero, input }) {
        const { age } = input
        return new Promise ((resolve, reject) => {
            HeroModel.update({name: hero}, {age}, (err, res) => {
                if(err) {
                    reject(err)
                    return
                }
                resolve(res)
            })
        })
    }
}

5. 模拟测试

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

mutation {
    createHero(input: {
        name: "钢铁侠"
        age: 40
    }){
        name
        age
    }
}
mutation {
    createHero(input: {
        name: "美国队长"
        age: 20
    }){
        name
        age
    }
}

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

在测试下查询:

query {
    hero {
        name
        age
    }
}

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

mutation {
    updateHero(hero: "美国队长", input: {
        age: 60
    }){
        age
    }
}

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


总结

  • GraphQL 是一种 API 的查询语言,是 REST API 的替代品。
  • GraphQL 可以使用一个请求,获取所有想要的数据。
  • 创建查询的方式有两种:使用 buildSchema 或者 GraphQLObjectType
  • 查询操作用 Query,修改操作用 Mutations
  • 查询类型用 type ,输入类型用 input

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


本文首发在 pingan8787个人博客,如需转载请保留个人介绍

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。   相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
目录
相关文章
|
开发框架 小程序 JavaScript
基于mpvue框架的小程序项目搭建入门教程一
基于mpvue框架的小程序项目搭建入门教程一
161 0
|
5月前
|
中间件 数据库连接 UED
Django中间件秘籍:如何用几行代码让你的应用变得超级强大?
【8月更文挑战第31天】中间件是Django框架的核心特性,位于视图与HTTP服务器之间,允许全局处理请求和响应,增强Web应用功能。通过实现`MiddlewareMixin`类的方法,如`process_request`和`process_response`,可以轻松实现请求预处理或响应后处理。中间件应用场景广泛,包括用户认证、CSRF防护和数据库连接管理等。创建并配置中间件需将其加入`settings.py`的`MIDDLEWARE`列表,顺序决定执行优先级。合理利用中间件能提高代码重用性和应用性能,带来更好的用户体验。
63 0
|
7月前
|
JavaScript 程序员 应用服务中间件
快速入门Web开发(上) 黑马程序员JavaWeb开发教程(2)
快速入门Web开发(上) 黑马程序员JavaWeb开发教程(2)
65 7
|
7月前
|
XML 存储 JavaScript
快速入门Web开发(上) 黑马程序员JavaWeb开发教程(1)
快速入门Web开发(上) 黑马程序员JavaWeb开发教程(1)
86 5
|
8月前
|
设计模式 前端开发 PHP
【PHP开发专栏】Laravel框架快速上手
【4月更文挑战第30天】本文介绍了Laravel,一个流行的PHP Web框架,以其优雅语法和简洁设计受开发者喜爱。内容分为三部分:1) 环境准备与项目创建,包括安装Composer和使用Laravel安装器创建新项目;2) 基本概念和核心组件,涉及路由、控制器、模型和视图的使用;3) 进阶功能与实战应用,如用户认证、表单验证和邮件发送。通过学习,读者可快速上手Laravel,进行高效Web应用开发。
98 0
|
Go API 网络架构
小试GraphQL
小试GraphQL
71 0
|
缓存 前端开发 JavaScript
【CuteJavaScript】GraphQL真香入门教程 上
【CuteJavaScript】GraphQL真香入门教程 上
196 0
|
移动开发 JavaScript 前端开发
强烈推荐 GitHub 上值得前端学习的开源实战项目
强烈推荐 GitHub 上值得前端学习的开源实战项目
2330 0
|
缓存 JavaScript 前端开发
再见 Swagger UI,国人开源了一款超好用的 API 文档生成框架,Star 4.7K+,真香……
最近,栈长发现某些国内的开源项目都使用到了 Knife4j 技术,看名字就觉得很锋利啊!
1024 0
再见 Swagger UI,国人开源了一款超好用的 API 文档生成框架,Star 4.7K+,真香……
【Go开源宝藏】Web框架 GIN 专场 (含思维导图) | 持续更新
【Go开源宝藏】Web框架 GIN 专场 (含思维导图) | 持续更新
363 0
【Go开源宝藏】Web框架 GIN 专场 (含思维导图) | 持续更新