【graphql】express+mongodb+mongoose制作graphql接口

4 篇文章 2 订阅
2 篇文章 0 订阅

前言

  • 最近学了graphql,感觉真香,这玩意必须要总结下!!!

准备工作

  • 首先创建项目,npm init ,然后需要安装下面这些包,这篇不写前台连接,cors也可以不用装。
cnpm i express graphql express-graphql mongoose  cors -D
  • 这次用的是Mongodb数据库,官网进行安装,这就不用说了。
  • 放个graphql官网文档,更多需求去官网看一下。
  • 至于不想用express,使用别的js后端,可以使用apollo-server,如果不想用js,参考这里

建立schema

  • 先使用假数据跑通再对接数据库。
const graphql = require('graphql')
const { 
    GraphQLObjectType,
    GraphQLString,
    GraphQLInt,
    GraphQLSchema,
    GraphQLList,
    GraphQLNonNull
} = graphql;

let categories = [//分类
    { id: '1', name: '吃的' },
    { id: '2', name: '喝的' },
    { id: '3', name: '用的' }
]
let products = [
    { id: '1', name: '浪味仙', category: '1' },
    { id: '2', name: '炸鸡', category: '1' },
    { id: '3', name: '旺旺仙贝', category: '1' },
    { id: '4', name: '汉堡', category: '1' },
    { id: '1', name: '茶颜悦色', category: '2' },
    { id: '2', name: '纸巾', category: '3' }
]
//分类里定义每个字段
const Category = new GraphQLObjectType({
    name :'category',
    fields:()=>({
        id:{type:GraphQLString},
        name:{type:GraphQLString},
        products:{
            type:new GraphQLList(Product),//每个分类下是一个数组,而不是一个对象
            resolve(parent){//parent代表上一层
                return products.filter(item=>item.category===parent.id)
            }
        }
    })
})
//产品里定义每个字段
const Product = new GraphQLObjectType({
    name :'product',
    fields:()=>({
        id:{type:GraphQLString},
        name:{type:GraphQLString},
        category:{
            type:Category,
            resolve(parent){
               return categories.find(item=>{
                   return item.id===parent.category
               })
            }
        }
    })
})

//查询接口
const RootQuery= new GraphQLObjectType({
    name:'root',
    fields:{
        getCategory:{
            type:Category,
            args:{//查询参数
                id:{
                    type:GraphQLString
                }
            },
            resolve(parent,args){
                return categories.find(item=>item.id===args.id)
            }
        },        
        getCategories:{
            type:new GraphQLList(Category),
            args:{
            },
            resolve(parent,args){
                return categories
            }
        },
        getProduct:{
            type:new GraphQLList(Product),//有可能一个id对多个商品
            args:{
                id:{
                    type :GraphQLString
                }
            },
            resolve(parent,args){
                return products.filter((item)=>item.id===args.id)
            }
        },
        getProducts:{
            type:new GraphQLList(Product),
            args:{},
            resolve(parent,args){
                return products
            }
        }
    }
})
//添加接口
const RootMutation = new GraphQLObjectType({
    name:'rootmutation',//这个属性是最后生成接口文档的mutation接口名字
    fields:{
        addCategory:{//添加类别需要给名字
            type:Category,
            args:{
                name:{type:new GraphQLNonNull(GraphQLString)}
            },
            resolve(parent,args){
                args.id = categories.length+1+''//自增1
                categories.push(args)
                return args
            }
        },
        addProduct:{//添加商品需要给名字和类别
            type:Product,
            args:{
                name:{
                    type:new GraphQLNonNull(GraphQLString)
                },
                category:{
                    type:new GraphQLNonNull(GraphQLString)
                }
            },
            resolve(parent,args){
                args.id=products.length+1+''
                products.push(args)
                return args   
            }
        },
        delProductByName:{
            type:new GraphQLList(Product),
            args:{
                category:{
                    type:new GraphQLNonNull(GraphQLString)
                },
                name:{
                    type:new GraphQLNonNull(GraphQLString)
                }
            },
            resolve(parent,args){
                products = products.reduce((prev,next)=>{
                    return (next.name===args.name&&next.category===args.category)?prev:prev.concat(next)
                },[])
                return  products
            }
        }
    }
})


module.exports = new GraphQLSchema({
    query: RootQuery,
    mutation:RootMutation
})

  • 我上面做了个假数据分类和产品,其中分类下面有产品,category字段的数字代表对应的分类。
  • 然后把分类和产品每个字段进行定义,也就是你想呈现给查询的人什么东西,就定义什么东西。值得注意的就是这里字段我想通过分类来查产品,就给分类下面加个产品字段,然后进行反查,这个反查逻辑写到resolve函数里,return的东西代表要展现给查询人看的东西。另外反查的type,需要结合实际情况来看,比如我这里分类下面去查产品,那么返回的每个分类下必然是很多个Product graphql对象组成的数组,所以这里要转成数组。而通过产品去查分类,必然只有一个分类对象,而不是分类数组,所以类型是给Category。
  • 这里定字段是函数形式,查询是直接对象。

express连接

  • 然后使用express把graphql连起来。
const express = require('express')
const graphqlHTTP=require('express-graphql')
const schema = require('./schema')
const cors = require('cors')
const app = express()
app.use(cors({
    origin:'http://localhost:3000',
    method:'GET,PUT,POST,OPTIONS'
}))
app.use('/graphql',graphqlHTTP({
    schema,
    graphiql:true
}))
app.listen(4000,()=>{
    console.log('server start');
})
  • 其中路径为/graphql就是graphiql的地址了,这个东西可以让我们对后台语句进行各种测试,还自带接口文档,真是太方便了,而自己想要啥数据就能获得啥数据。
    在这里插入图片描述

  • 只要查询语句什么格式,他就能给你返回什么格式,只要id,就写id,绝不多反给你,想嵌套,就写多少层,绝不少。

  • 下面看一下查询语法和修改语法:

{
  getProduct(id:"1"){
    id,name
  }
}
  • 查询有参数的就在小括号里写上参数冒号参数,如果是graphqlstring类型,必须使用双引号。
  • 花括号里就是你想返回的字段了。
{
	getCategories{
    id,
    name,
    products{
      id,name
    }
  }
}
  • 无参数查询则不需要写小括号,花括号里写想要的字段,如果有嵌套可以继续花括号。
mutation {
  delProductByName(name: "可乐3", category: "2") {
    id
    name
    category {
      id
      name
      products {
        id
        name
      }
    }
  }
}
  • 修改数据前面需要加上mutation ,其他跟上面一样。

加入Mongodb数据库

  • 可以发现真正需要改的地方就只有resolve,把resolve逻辑改成操作数据库即可完成。

model.js

let mongoose = require('mongoose')
let ObjectId = mongoose.Schema.Types.ObjectId
const Schema = mongoose.Schema
const conn =mongoose.createConnection('mongodb://localhost/graphql', { useNewUrlParser: true, useUnifiedTopology: true })
conn.on('error',(err)=>{
    console.log(err);
})
conn.on('open',()=>{
    console.log('ok');
})
const CategorySchema = new Schema({
    name:String
})
const CategoryModel = conn.model('Category',CategorySchema)

const  ProductSchema = new Schema({
    name:String,
    category:{
        type:ObjectId, //产品的分类字段是分类的objid字段 
        ref:'Category'
    }
})
const ProductModel = conn.model('Product',ProductSchema)
module.exports={
    CategoryModel,
    ProductModel
};
  • 连接数据库,建立schema和model,跟graphql的schema一样,也是2个,但是字段有点不一样,这个代表存储的数据,id由mongo自带的objectId完成。建立外键由products的schema做引用category的objid即可。

schema.js

const graphql = require('graphql')
const{ProductModel,CategoryModel }=require('./model')
let mongoose = require('mongoose')
const { 
    GraphQLObjectType,
    GraphQLString,
    GraphQLInt,
    GraphQLSchema,
    GraphQLList,
    GraphQLNonNull
} = graphql;

let categories = [//分类
    { id: '1', name: '吃的' },
    { id: '2', name: '喝的' },
    { id: '3', name: '用的' }
]
let products = [
    { id: '1', name: '浪味仙', category: '1' },
    { id: '2', name: '炸鸡', category: '1' },
    { id: '3', name: '旺旺仙贝', category: '1' },
    { id: '4', name: '汉堡', category: '1' },
    { id: '1', name: '茶颜悦色', category: '2' },
    { id: '2', name: '纸巾', category: '3' }
]
//分类里定义每个字段
const Category = new GraphQLObjectType({
    name :'category',
    fields:()=>({
        id:{type:GraphQLString},
        name:{type:GraphQLString},
        products:{
            type:new GraphQLList(Product),//每个分类下是一个数组,而不是一个对象
            resolve(parent){//parent代表上一层
                //return products.filter(item=>item.category===parent.id)
                return ProductModel.find({category:parent.id})
            }
        }
    })
})
//产品里定义每个字段
const Product = new GraphQLObjectType({
    name :'product',
    fields:()=>({
        id:{type:GraphQLString},
        name:{type:GraphQLString},
        category:{
            type:Category,
            resolve(parent){
            //    return categories.find(item=>{
            //        return item.id===parent.category
            //    })
                return CategoryModel.findById(parent.category)
            }
        }
    })
})

//查询接口
const RootQuery= new GraphQLObjectType({
    name:'root',
    fields:{
        getCategory:{
            type:Category,
            args:{//查询参数
                id:{
                    type:GraphQLString
                }
            },
            resolve(parent,args){
           //     return categories.find(item=>item.id===args.id)
                return CategoryModel.findById(args.id)
            }
        },        
        getCategories:{
            type:new GraphQLList(Category),
            args:{
            },
            resolve(parent,args){
            //    return categories
                return CategoryModel.find()
            }
        },
        getProduct:{
            type:new GraphQLList(Product),
            args:{
                id:{
                    type :GraphQLString
                }
            },
            resolve(parent,args){
                //return products.filter((item)=>(item.id===args.id))
                return ProductModel.findById(args.id)
            }
        },
        getProducts:{
            type:new GraphQLList(Product),
            args:{},
            resolve(parent,args){
               // return products
               return ProductModel.find()
            }
        }
    }
})
//添加接口
const RootMutation = new GraphQLObjectType({
    name:'rootmutation',
    fields:{
        addCategory:{//添加类别需要给名字
            type:Category,
            args:{
                name:{type:new GraphQLNonNull(GraphQLString)}
            },
            resolve(parent,args){
                // args.id = categories.length+1+''//自增1
                // categories.push(args)
                // return args
                return CategoryModel.create(args)
            }
        },
        addProduct:{//添加商品需要给名字和类别
            type:Product,
            args:{
                name:{
                    type:new GraphQLNonNull(GraphQLString)
                },
                category:{
                    type:new GraphQLNonNull(GraphQLString)
                }
            },
            resolve(parent,args){
                // args.id=products.length+1+''
                // products.push(args)
                // return args   
                return ProductModel.create(args)
            }
        },
        delProductByName:{
            type:new GraphQLList(Product),
            args:{
                category:{
                    type:new GraphQLNonNull(GraphQLString)
                },
                name:{
                    type:new GraphQLNonNull(GraphQLString)
                }
            },
             async resolve(parent,args){
                // products = products.reduce((prev,next)=>{
                //     return (next.name===args.name&&next.category===args.category)?prev:prev.concat(next)
                // },[])
                // return products
                let res=await ProductModel.deleteMany(args)
                return  ProductModel.find()           
            }
        }
    }
})


module.exports = new GraphQLSchema({
    query: RootQuery,
    mutation:RootMutation
})
  • 可以发现,mongoose,Mongodb与express接近完全分离,靠graphql来驱动,前面写的server.js完全不变。让代码看上去更加清爽。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

业火之理

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值