
掌握API建模:基本概念和實踐
在過去的十多年中,REST 已經成為設計 web api 的標準(雖然只是一個模糊的標準)。
它提供了一些很棒的想法,比如無狀態服務器和結構化的資源訪問。
然而 REST api 表 現得過于僵化,無法跟上訪問它們的客戶的快速變化的需求
客戶端可以自定義 Api 聚合
如果設計的數據結構是從屬的,直接就能在查詢語句中指定;即使數據結構是獨 立的,也可以在查詢語句中指定上下文,只需要一次網絡請求,就能獲得資源和子 資源的數據。
代碼即是文檔
GraphQL 會把 schema 定義和相關的注釋生成可視化的文檔,從而使得代碼的變更,直接就反映到最新的文檔上,避免 RESTful 中手工維護可能會造成代碼、 文檔不一致的問題
參數類型強校驗
可以將GraphQL的類型系統分為
標量類型
(ScalarTypes,標量類型)和其他高級數據類型
,標量類型即可以表示最細粒度數據結構的數據類型,可以和JavaScript的原始類型對應
GraphQL規范目前規定支持的標量類型有
32
位整數 — GraphQLInt
GraphQLFloat
UTF‐8
字符序列 — GraphQLString
true
或者false
— GraphQLBoolean
GraphQL其他高級數據類型包括
用于描述層級或者樹形數據結構。對于樹形數據結構來說,葉子字段的類型都是標量數據類型。幾乎所有GraphQL類型都是對象類型。Object類型有一個name字段,以及一個很重要的fields字段。fields字段可以描述出一個完整的數據結構。例如一個表示地址數據結構的GraphQL對象為
const?AddressType=newGraphQLObjectType({
????name:'Address',
????fields:{
????????street:{
????????????type:GraphQLString
????????},
????????number:{
????????????type:GraphQLInt
????????},
????????formatted:{
????????????type:GraphQLString,
????????????resolve(obj){
????????????????return?obj.number+''+obj.street
????????????}???
????????}
????}
});
Interface
:接口用于描述多個類型的通用字Union
:聯合類型用于描述某個字段能夠支持的所有返回類型以及具體請求真正的返回類型Enum
:枚舉用于表示可枚舉數據結構的類型InputObject
:輸入對象List
:列表列表是其他類型的封裝,通常用于對象字段的描述。例如下面PersonType類型數據的parents和children字段
const?PersonType=newGraphQLObjectType({
????name:'Person',
????fields:()=>({
????????parents:{type:newGraphQLList(Person)},
????????children:{type:newGraphQLList(Person)},
????})
})
Non-Null
:不能為Null
Non-Null
強制類型的值不能為null
,并且在請求出錯時一定會報錯。可以用于必須保證值不能為null
的字段。例如數據庫的行的id字段不能為null
const?RowType=newGraphQLObjectType({
????name:'Row',
????fields:()=>({
????????id:{
????????????type:newGraphQLNonNull(GraphQLString)
????????}
????})
})
GraphQL規范支持兩種操作
query
:僅獲取數據(fetch
)的只讀請求mutation
:獲取數據后還有寫操作的請求新版本的GraphQL還支持
subscription
,這是為了處理訂閱更新這種比較復雜的實時數據更新場景而設計的操作
使用mongodb
做數據庫演示,mac安裝mongodb
,brew install mongodb-community
#?進入mongo?shell
mongo?
#?創建數據庫
use?graphql?(graphql數據庫不存在會自動創建)
#?創建nav、articlecate集合插入數據
db.nav.insert({name:?"標題1",?url:?"/",?sort:?1,?add_time:?"2022-06-30"})
db.nav.insert({name:?"標題2",?url:?"/",?sort:?1,?add_time:?"2022-06-30"})
db.nav.insert({name:?"標題3",?url:?"/",?sort:?1,?add_time:?"2022-06-30"})
db.articlecate.insert({name:?"分類1",?description:?"描述",?keywords:?"關鍵詞",?status:?1})
db.articlecate.insert({name:?"分類2",?description:?"描述",?keywords:?"關鍵詞",?status:?1})
db.articlecate.insert({name:?"分類3",?description:?"描述",?keywords:?"關鍵詞",?status:?1})
或者導入數據庫數據
下載數據庫文件解壓并導入mongodb即可 https://blog.poetries.top/db/koa.zip
導入mongodb數據庫
mongorestore?-h?localhost:27017?-d?koa-demo(數據庫名稱,不存在會自動創建)?./dump(本地數據文件路徑)
https://github.com/graphql/express-graphql
npm?install?express-graphql?graphql--save
引入express-graphql配置中間件
//?app.js
var?express=require('express');
var?DB=require('./model/db.js');?
const?graphqlHTTP?=?require('express-graphql');
const?GraphQLDefaultSchema?=?require('./schema/default.js');
var?app=express();?
//?配置中間件
app.use('/graphql',?graphqlHTTP({
????schema:?GraphQLDefaultSchema,
????graphiql:?true?//?線上環境關閉,開發環境開啟
}));
//配置路由
app.get('/',function(req,res){
????res.send('hello?express');
})
app.listen(3000,()=>console.log("http://localhost:3000"));
schema/default.js
Schema
const?DB=require('../model/db.js');?/*引入DB庫*/
const?{
????GraphQLObjectType,
????GraphQLString,
????GraphQLInt,
????GraphQLSchema,
????GraphQLList
}?=require('graphql')
//1、獲取導航列表?????定義導航的schema類型
var?NavSchema=new?GraphQLObjectType({
????name:'nav',
????fields:{
????????_id:{
????????????type:GraphQLString
????????},
????????title:{
????????????type:GraphQLString
????????},
????????url:{
????????????type:GraphQLString
????????},
????????sort:{
????????????type:GraphQLInt
????????},
????????status:{
????????????type:GraphQLInt
????????},
????????add_time:{
????????????type:GraphQLString
????????}
????}
})
var?ArticleCateSchema=new?GraphQLObjectType({
????name:'articlecate',
????fields:{
????????_id:{
????????????type:GraphQLString
????????},
????????title:{
????????????type:GraphQLString
????????},
????????description:{
????????????type:GraphQLString
????????},
????????keywords:{
????????????type:GraphQLString
????????}?,?
????????status:{
????????????type:GraphQLInt
????????}?
????}
})
//2、定義一個跟???????根里面定義調用導航Schema類型的方法
var?RootSchema=new?GraphQLObjectType({
????name:'root',
????fields:{
????????oneNavList:{??//方法名稱:定義調用導航Schema類型的方法
????????????type:NavSchema,??//方法的類型,?方法返回的參數必須和NavSchema里面定義的類型一致
????????????args:{id:{type:GraphQLString}},?//參數
????????????async?resolve(parent,args){??//執行的操作
????????????????//?args.id?獲取調用方法傳入的值
????????????????var?id=args.id;
????????????????var?navList=await?DB.find('nav',{"_id":DB.getObjectId(id)});
????????????????return?navList[0];???????????????
????????????}
????????},
????????navList:{
????????????type:GraphQLList(NavSchema),??
????????????async?resolve(parent,args){???????????????
????????????????var?navList=await?DB.find('nav',{});
????????????????return?navList;???????????????
????????????}
????????},
????????articleCateList:{
????????????type:GraphQLList(ArticleCateSchema),
????????????async?resolve(parent,args){???????????????
????????????????var?articlecateList=await?DB.find('articlecate',{});
????????????????return?articlecateList;?
????????????}
????????},
????????oneArticleCateList:{
????????????type:ArticleCateSchema,?
????????????args:{id:{type:GraphQLString}},
????????????async?resolve(parent,args){?????????????
????????????????var?id=args.id;
????????????????var?articlecateList=await?DB.find('articlecate',{"_id":DB.getObjectId(id)});
????????????????return?articlecateList[0];???//要返回一個json對象
????????????}
????????}
????}
})
//3、把根掛載到?GraphQLSchema
module.exports=new?GraphQLSchema({
????query:RootSchema
})
/**
?*?http://mongodb.github.io/node-mongodb-native
?*?http://mongodb.github.io/node-mongodb-native/3.0/api/
?*/
//DB庫
var?MongoDB=require('mongodb');
var?MongoClient?=MongoDB.MongoClient;
const?ObjectID?=?MongoDB.ObjectID;
var?Config=?{
????dbUrl:?'mongodb://localhost:27017/',
????dbName:?'graphql'?//?數據庫名
}
class?Db?{
????static?getInstance(){???/*1、單例??多次實例化實例不共享的問題*/
????????if(!Db.instance){
????????????Db.instance=new?Db();
????????}
????????return??Db.instance;
????}
????constructor(){
????????this.dbClient='';?/*屬性?放db對象*/
????????this.connect();???/*實例化的時候就連接數據庫*/
????}
????connect(){??/*連接數據庫*/
??????let?_that=this;
??????return?new?Promise((resolve,reject)=>{
??????????if(!_that.dbClient){?????????/*1、解決數據庫多次連接的問題*/
??????????????MongoClient.connect(Config.dbUrl,{?useNewUrlParser:?true?},(err,client)=>{
??????????????????if(err){
????????????????????reject(err)
??????????????????}else{
????????????????????_that.dbClient=client.db(Config.dbName);
????????????????????resolve(_that.dbClient)
??????????????????}
??????????????})
??????????}else{
????????????resolve(_that.dbClient);
??????????}
??????})
????}
????/*
?????DB.find('user',{})??返回所有數據
?????DB.find('user',{},{"title":1})????返回所有數據??只返回一列
?????DB.find('user',{},{"title":1},{???返回第二頁的數據
????????page:2,
????????pageSize:20,
????????sort:{"add_time":-1}
?????})
?????js中實參和形參可以不一樣??????arguments?對象接收實參傳過來的數據
????*?*/
????find(collectionName,json1,json2,json3){
????????if(arguments.length==2){
????????????var?attr={};
????????????var?slipNum=0;
????????????var?pageSize=0;
????????}else?if(arguments.length==3){
????????????var?attr=json2;
????????????var?slipNum=0;
????????????var?pageSize=0;
????????}else?if(arguments.length==4){
????????????var?attr=json2;
????????????var?page=parseInt(json3.page)?||1;
????????????var?pageSize=parseInt(json3.pageSize)||20;
????????????var?slipNum=(page-1)*pageSize;
????????????if(json3.sort){
????????????????var?sortJson=json3.sort;
????????????}else{
????????????????var?sortJson={}
????????????}
????????}else{
????????????console.log('傳入參數錯誤')
????????}
???????return?new?Promise((resolve,reject)=>{
????????????this.connect().then((db)=>{
????????????????//var?result=db.collection(collectionName).find(json);
????????????????var?result?=db.collection(collectionName).find(json1,{fields:attr}).skip(slipNum).limit(pageSize).sort(sortJson);
????????????????result.toArray(function(err,docs){
????????????????????if(err){
????????????????????????reject(err);
????????????????????????return;
????????????????????}
????????????????????resolve(docs);
????????????????})
????????????})
????????})
????}
????update(collectionName,json1,json2){
????????return?new?Promise((resolve,reject)=>{
????????????this.connect().then((db)=>{
????????????????//db.user.update({},{$set:{}})
????????????????db.collection(collectionName).updateOne(json1,{
????????????????????$set:json2
????????????????},(err,result)=>{
????????????????????if(err){
????????????????????????reject(err);
????????????????????}else{
????????????????????????resolve(result);
????????????????????}
????????????????})
????????????})
????????})
????}
????insert(collectionName,json){
????????return?new??Promise((resolve,reject)=>{
????????????this.connect().then((db)=>{
????????????????db.collection(collectionName).insertOne(json,function(err,result){
????????????????????if(err){
????????????????????????reject(err);
????????????????????}else{
????????????????????????resolve(result);
????????????????????}
????????????????})
????????????})
????????})
????}
????remove(collectionName,json){
????????return?new??Promise((resolve,reject)=>{
????????????this.connect().then((db)=>{
????????????????db.collection(collectionName).removeOne(json,function(err,result){
????????????????????if(err){
????????????????????????reject(err);
????????????????????}else{
????????????????????????resolve(result);
????????????????????}
????????????????})
????????????})
????????})
????}
????getObjectId(id){??/*mongodb里面查詢?_id?把字符串轉換成對象*/
????????return?new?ObjectID(id);
????}
????//統計數量的方法
????count(collectionName,json){
????????return?new??Promise((resolve,reject)=>?{
????????????this.connect().then((db)=>?{
????????????????var?result?=?db.collection(collectionName).count(json);
????????????????result.then(function?(count)?{
????????????????????????resolve(count);
????????????????????}
????????????????)
????????????})
????????})
????}
}
module.exports=Db.getInstance();
打開本地調試
http://localhost:3000/graphql
下載數據庫文件解壓并導入mongodb即可 https://blog.poetries.top/db/koa.zip
mongorestore -h localhost:27017 -d koa-demo(數據庫名稱,不存在會自動創建) ./dump(本地數據文件路徑)
mongodump -h localhost:27017 -d test(數據庫名稱) -o ./dump
文檔地址 https://github.com/chentsulin/koa-graphql
npm?install?graphql?koa-graphql?koa-mount?--save
實現導航列表API、文章分類API、文章列表API、文章詳情API 、文章列表分頁查詢API、以及文章列表關聯文章分類實現聚合API
//?app.js?
var?Koa=require('koa');
var?router?=?require('koa-router')();?
const?mount?=?require('koa-mount');
const?graphqlHTTP?=?require('koa-graphql');
var?GraphQLDefaultSchema=require('./schema/default.js')
const?DB=require('./model/db.js');
var?app=new?Koa();
//配置中間件
app.use(mount('/graphql',?graphqlHTTP({
????schema:?GraphQLDefaultSchema,
????graphiql:?true
})));
router.get('/',async?(ctx)=>{
????ctx.body="首頁";
})
router.get('/getNavList',async?(ctx)=>{
????
????var?navList=await?DB.find('nav',{});?????
?????ctx.body=navList;
})??
app.use(router.routes());???/*啟動路由*/
app.use(router.allowedMethods());
app.listen(4000,?()=>console.log('http://localhost:4000'));
//?schema/default.js
const?DB=require('../model/db.js');
//文章分類api接口?????//文章列表api接口?(分頁)?????//文章詳情api接口(api聚合?獲取分類信息)
const?{
????GraphQLObjectType,
????GraphQLString,
????GraphQLInt,
????GraphQLFloat,
????GraphQLList,
????GraphQLSchema,
????GraphQLID
}=require('graphql')
//1、定義導航的schema
var?NavSchema=new?GraphQLObjectType({
????name:'nav',
????fields:{
????????_id:{
????????????type:GraphQLString
????????},
????????title:{
????????????type:GraphQLString
????????},url:{
????????????type:GraphQLString
????????},
????????sort:{
????????????type:GraphQLInt
????????},
????????status:{
????????????type:GraphQLString
????????},
????????add_time:{
????????????type:GraphQLString
????????}
????}
})
//定義文章分類的schema
var?ArticleCateSchema=new?GraphQLObjectType({
????name:'articlecate',
????fields:{
????????_id:{type:GraphQLString},
????????title:{type:GraphQLString},
????????description:{?type:?GraphQLString?},
????????keywords:{?type:?GraphQLInt?},
????????pid:{type:GraphQLInt},
????????add_time:{?type:?GraphQLString?},
????????status:{?type:?GraphQLInt?}??????
????}
})
//定義文章的schema
var?ArticleSchema=new?GraphQLObjectType({
????name:'article',
????fields:{
????????_id:{type:GraphQLID},
????????pid:{type:GraphQLID},????
????????title:{?type:?GraphQLString?},
????????author:{?type:?GraphQLString?},
????????status:{type:GraphQLInt},
????????is_best:{?type:?GraphQLInt?},
????????is_hot:{?type:?GraphQLInt?},
????????is_new:{?type:?GraphQLInt?},
????????keywords:{?type:?GraphQLString?},
????????description:{?type:?GraphQLString?},
????????content:{?type:?GraphQLString?},
????????sort:{?type:?GraphQLInt?},
????????//?聚合查詢文章分類信息
????????cateInfo:{
????????????type:ArticleCateSchema,
????????????async?resolve(parent,args){
????????????????//?parent.pid?當前新聞的分類id
????????????????console.log(parent);
????????????????var?cateResult=await?DB.find('articlecate',{"_id":DB.getObjectId(parent.pid)});
????????????????return?cateResult[0];
????????????}
????????}
????}
})
//訂單商品的Schema??(order_item)
var?OrderItem=new?GraphQLObjectType({
????name:'orderitem',
????fields:{
????????uid:{?type:?GraphQLID?},
????????order_id:??{?type:?GraphQLID?},
????????product_title:?{?type:?GraphQLString?},
????????product_id:?{?type:?GraphQLID?},????
????????product_img:?{?type:?GraphQLString?},????
????????product_price:?{?type:?GraphQLFloat?},??
????????product_num:?{?type:?GraphQLInt?},????????
????????add_time:?{
??????????type:?GraphQLString????????
????????}??????
????}
})
//訂單的Schema
var?OrderSchema=new?GraphQLObjectType({
????name:'order',
????fields:{
????????_id:{type:GraphQLID},
????????uid:?{?type:GraphQLID},
????????all_price:?{?type:?GraphQLInt?},
????????order_id:?{?type:?GraphQLInt?},
????????name:?{?type:?GraphQLString?},??
????????phone:?{?type:?GraphQLString?},????
????????address:??{?type:?GraphQLString?},????
????????zipcode:??{?type:?GraphQLString?},????
????????pay_status:{?type:?GraphQLInt},???//?支付狀態:?0?表示未支付???? 1 已經支付
????????pay_type:{type:?GraphQLString},??????//?支付類型:alipay ?? wechat ?
????????order_status:?{???????????????//?訂單狀態:?0?已下單? 1 已付款? 2 已配貨? 3、發貨?? 4、交易成功?? 5、退貨???? 6、取消??????
??????????type:?GraphQLInt??????
????????},
????????add_time:?{
??????????type:?GraphQLString??????????
????????},
????????//?聚合查詢訂單關聯的商品列表信息
????????orderItems:{
????????????type:GraphQLList(OrderItem),
????????????async?resolve(parent,args){
????????????????//獲取當前訂單對應的商品?parent._id就是objectId
????????????????var?orderItemList=await?DB.find('order_item',{"order_id":parent._id});
????????????????return?orderItemList;
????????????}
????????}
????}
})
//2、定義一個根?配置調用Schema的方法
var?RootSchema=new?GraphQLObjectType({
????name:'root',
????fields:{
????????navList:{
????????????type:GraphQLList(NavSchema),
????????????async?resolve(parent,args){
????????????????var?navList=await?DB.find('nav',{});?????
????????????????return?navList;
????????????}????????????
????????},
????????oneNavList:{
????????????type:NavSchema,
????????????args:{
????????????????_id:{
????????????????????type:GraphQLString
????????????????},
????????????????status:{
????????????????????type:GraphQLString
????????????????}
????????????},
????????????async?resolve(parent,args){
????????????????var?oneNavList=await?DB.find('nav',{"_id":DB.getObjectId(args._id),"status":args.status});?????
????????????????return?oneNavList[0];
????????????}
????????},
????????articleCateList:{
????????????type:GraphQLList(ArticleCateSchema),
????????????async?resolve(parent,args){
????????????????var?articlecateList=await?DB.find('articlecate',{});?????
????????????????return?articlecateList;
????????????}
????????},
????????articleList:{
????????????type:GraphQLList(ArticleSchema),
????????????args:{
????????????????page:{
????????????????????type:GraphQLInt
????????????????},
????????????????pageSize:{
????????????????????type:GraphQLInt
????????????????}
????????????},
????????????//?分頁查詢文章列表
????????????async?resolve(parent,args){
????????????????var?page=args.page||1;
????????????????var?pageSize=args.pageSize||5;
????????????????console.log(page,pageSize);
????????????????var?articleList=await?DB.find('article',{},{},{
????????????????????page,
????????????????????pageSize:pageSize,
????????????????????sort:{"add_time":-1}
?????????????????});?????
????????????????return?articleList;
????????????}
????????},
????????//?訂單列表
????????orderList:{
????????????type:GraphQLList(OrderSchema),
????????????args:{
????????????????page:{
????????????????????type:GraphQLInt
????????????????}
????????????},
????????????async?resolve(parent,args){
????????????????var?page=args.page?||?1;
????????????????var?orderList=await?DB.find('order',{},{},{
????????????????????page,
????????????????????pageSize:3????????????????????
?????????????????});?????
????????????????return?orderList;
????????????}
????????},
????????//?單個訂單信息
????????oneOrderList:{
????????????type:OrderSchema,
????????????args:{
????????????????_id:{
????????????????????type:GraphQLID
????????????????}
????????????},
????????????async?resolve(parent,args){???????????????
????????????????var?orderList=await?DB.find('order',{"_id":DB.getObjectId(args._id)});?????
????????????????return?orderList[0];
????????????}
????????}
????}
})
//3、把查詢的根?掛載到GraphQLSchema
module.exports=new?GraphQLSchema({
????query:RootSchema
})
//?model/db.js
/**
?*?http://mongodb.github.io/node-mongodb-native
?*?http://mongodb.github.io/node-mongodb-native/3.0/api/
?*/
//DB庫
var?MongoDB=require('mongodb');
var?MongoClient?=MongoDB.MongoClient;
const?ObjectID?=?MongoDB.ObjectID;
var?Config=?{
????url:?'mongodb://localhost:27017',
????dbName:?'koa-demo'
}
class?Db{
????static?getInstance(){???/*1、單例??多次實例化實例不共享的問題*/
????????if(!Db.instance){
????????????Db.instance=new?Db();
????????}
????????return??Db.instance;
????}
????constructor(){
????????this.dbClient='';?/*屬性?放db對象*/
????????this.connect();???/*實例化的時候就連接數據庫*/
????}
????connect(){??/*連接數據庫*/
??????let?_that=this;
??????return?new?Promise((resolve,reject)=>{
??????????if(!_that.dbClient){?????????/*1、解決數據庫多次連接的問題*/
??????????????MongoClient.connect(Config.dbUrl,{?useNewUrlParser:?true?},(err,client)=>{
??????????????????if(err){
??????????????????????reject(err)
??????????????????}else{
??????????????????????_that.dbClient=client.db(Config.dbName);
??????????????????????resolve(_that.dbClient)
??????????????????}
??????????????})
??????????}else{
????????????resolve(_that.dbClient);
??????????}
??????})
????}
????/*
?????DB.find('user',{})??返回所有數據
?????DB.find('user',{},{"title":1})????返回所有數據??只返回一列
?????DB.find('user',{},{"title":1},{???返回第二頁的數據
????????page:2,
????????pageSize:20,
????????sort:{"add_time":-1}
?????})
?????js中實參和形參可以不一樣??????arguments?對象接收實參傳過來的數據
????*?*/
????find(collectionName,json1,json2,json3){
????????if(arguments.length==2){
????????????var?attr={};
????????????var?slipNum=0;
????????????var?pageSize=0;
????????}else?if(arguments.length==3){
????????????var?attr=json2;
????????????var?slipNum=0;
????????????var?pageSize=0;
????????}else?if(arguments.length==4){
????????????var?attr=json2;
????????????var?page=parseInt(json3.page)?||1;
????????????var?pageSize=parseInt(json3.pageSize)||20;
????????????var?slipNum=(page-1)*pageSize;
????????????if(json3.sort){
????????????????var?sortJson=json3.sort;
????????????}else{
????????????????var?sortJson={}
????????????}
????????}else{
????????????console.log('傳入參數錯誤')
????????}
???????return?new?Promise((resolve,reject)=>{
????????????this.connect().then((db)=>{
????????????????//var?result=db.collection(collectionName).find(json);
????????????????var?result?=db.collection(collectionName).find(json1,{fields:attr}).skip(slipNum).limit(pageSize).sort(sortJson);
????????????????result.toArray(function(err,docs){
????????????????????if(err){
????????????????????????reject(err);
????????????????????????return;
????????????????????}
????????????????????resolve(docs);
????????????????})
????????????})
????????})
????}
????update(collectionName,json1,json2){
????????return?new?Promise((resolve,reject)=>{
????????????????this.connect().then((db)=>{
????????????????????//db.user.update({},{$set:{}})
????????????????????db.collection(collectionName).updateOne(json1,{
????????????????????????$set:json2
????????????????????},(err,result)=>{
????????????????????????if(err){
????????????????????????????reject(err);
????????????????????????}else{
????????????????????????????resolve(result);
????????????????????????}
????????????????????})
????????????????})
????????})
????}
????insert(collectionName,json){
????????return?new??Promise((resolve,reject)=>{
????????????this.connect().then((db)=>{
????????????????db.collection(collectionName).insertOne(json,function(err,result){
????????????????????if(err){
????????????????????????reject(err);
????????????????????}else{
????????????????????????resolve(result);
????????????????????}
????????????????})
????????????})
????????})
????}
????remove(collectionName,json){
????????return?new??Promise((resolve,reject)=>{
????????????this.connect().then((db)=>{
????????????????db.collection(collectionName).removeOne(json,function(err,result){
????????????????????if(err){
????????????????????????reject(err);
????????????????????}else{
????????????????????????resolve(result);
????????????????????}
????????????????})
????????????})
????????})
????}
????getObjectId(id){????/*mongodb里面查詢?_id?把字符串轉換成對象*/
????????return?new?ObjectID(id);
????}
????//統計數量的方法
????count(collectionName,json){
????????return?new??Promise((resolve,reject)=>?{
????????????this.connect().then((db)=>?{
????????????????var?result?=?db.collection(collectionName).count(json);
????????????????result.then(function?(count)?{
????????????????????????resolve(count);
????????????????????}
????????????????)
????????????})
????????})
????}
}
module.exports=Db.getInstance();
啟動服務
聚合查詢文章分類信息,分類信息的方式要放在article的schema里面,這樣才能聚合查詢到
聚合查詢結果
查詢訂單,聚合查詢訂單關聯的商品信息返回
//?schema/default.js?
//訂單商品的Schema??(order_item)
var?OrderItem=new?GraphQLObjectType({
????name:'orderitem',
????fields:{
????????uid:{?type:?GraphQLID?},
????????order_id:??{?type:?GraphQLID?},
????????product_title:?{?type:?GraphQLString?},
????????product_id:?{?type:?GraphQLID?},????
????????product_img:?{?type:?GraphQLString?},????
????????product_price:?{?type:?GraphQLFloat?},??
????????product_num:?{?type:?GraphQLInt?},????????
????????add_time:?{
??????????type:?GraphQLString????????
????????}??????
????}
})
//訂單的Schema
var?OrderSchema=new?GraphQLObjectType({
????name:'order',
????fields:{
????????_id:{type:GraphQLID},
????????uid:?{?type:GraphQLID},
????????all_price:?{?type:?GraphQLInt?},
????????order_id:?{?type:?GraphQLInt?},
????????name:?{?type:?GraphQLString?},??
????????phone:?{?type:?GraphQLString?},????
????????address:??{?type:?GraphQLString?},????
????????zipcode:??{?type:?GraphQLString?},????
????????pay_status:{?type:?GraphQLInt},???//?支付狀態:?0?表示未支付???? 1 已經支付
????????pay_type:{type:?GraphQLString},??????//?支付類型:alipay ?? wechat ?
????????order_status:?{???????????????//?訂單狀態:?0?已下單? 1 已付款? 2 已配貨? 3、發貨?? 4、交易成功?? 5、退貨???? 6、取消??????
??????????type:?GraphQLInt??????
????????},
????????add_time:?{
??????????type:?GraphQLString??????????
????????},
????????//?聚合查詢訂單關聯的商品列表信息
????????orderItems:{
????????????type:GraphQLList(OrderItem),
????????????async?resolve(parent,args){
????????????????//獲取當前訂單對應的商品?parent._id就是objectId
????????????????var?orderItemList=await?DB.find('order_item',{"order_id":parent._id});
????????????????return?orderItemList;
????????????}
????????}
????}
})
//?定義一個根?配置調用Schema的方法
var?RootSchema=new?GraphQLObjectType({
????name:'root',
????fields:{
????????orderList:{
????????????type:GraphQLList(OrderSchema),
????????????args:{
????????????????page:{
????????????????????type:GraphQLInt
????????????????}
????????????},
????????????async?resolve(parent,args){
????????????????var?page=args.page?||?1;
????????????????var?orderList=await?DB.find('order',{},{},{
????????????????????page,
????????????????????pageSize:3????????????????????
?????????????????});?????
????????????????return?orderList;
????????????}
????????},
????????oneOrderList:{
????????????type:OrderSchema,
????????????args:{
????????????????_id:{
????????????????????type:GraphQLID
????????????????}
????????????},
????????????async?resolve(parent,args){???????????????
????????????????var?orderList=await?DB.find('order',{"_id":DB.getObjectId(args._id)});?????
????????????????return?orderList[0];
????????????}
????????}
????}
})
查詢訂單詳情
需要哪些字段,就返回哪些字段,編輯器會自定提示
//定義文章分類的schema
var?ArticleCateSchema=new?GraphQLObjectType({
????name:'articlecate',
????fields:{
????????_id:{type:GraphQLString},
????????title:{type:GraphQLString},
????????description:{?type:?GraphQLString?},
????????keywords:{?type:?GraphQLInt?},
????????pid:{type:GraphQLInt},
????????add_time:{?type:?GraphQLString?},
????????status:{?type:?GraphQLInt?}??????
????}
})
//定義文章的schema
var?ArticleSchema=new?GraphQLObjectType({
????name:'article',
????fields:{
????????_id:{type:GraphQLID},
????????pid:{type:GraphQLID},????
????????title:{?type:?GraphQLString?},
????????author:{?type:?GraphQLString?},
????????status:{type:GraphQLInt},
????????is_best:{?type:?GraphQLInt?},
????????is_hot:{?type:?GraphQLInt?},
????????is_new:{?type:?GraphQLInt?},
????????keywords:{?type:?GraphQLString?},
????????description:{?type:?GraphQLString?},
????????content:{?type:?GraphQLString?},
????????sort:{?type:?GraphQLInt?},
????????//?聚合查詢文章分類信息
????????cateInfo:{
????????????type:ArticleCateSchema,
????????????async?resolve(parent,args){
????????????????//?parent.pid?當前新聞的分類id
????????????????console.log(parent);
????????????????var?cateResult=await?DB.find('articlecate',{"_id":DB.getObjectId(parent.pid)});
????????????????return?cateResult[0];
????????????}
????????}
????}
})
//2、定義一個根?配置調用Schema的方法
var?RootSchema=new?GraphQLObjectType({
????name:'root',
????fields:{
????????articleCateList:{
????????????type:GraphQLList(ArticleCateSchema),
????????????async?resolve(parent,args){
????????????????var?articlecateList=await?DB.find('articlecate',{});?????
????????????????return?articlecateList;
????????????}
????????},
????????articleList:{
????????????type:GraphQLList(ArticleSchema),
????????????args:{
????????????????page:{
????????????????????type:GraphQLInt
????????????????},
????????????????pageSize:{
????????????????????type:GraphQLInt
????????????????}
????????????},
????????????//?分頁查詢文章列表
????????????async?resolve(parent,args){
????????????????var?page=args.page||1;
????????????????var?pageSize=args.pageSize||5;
????????????????console.log(page,pageSize);
????????????????var?articleList=await?DB.find('article',{},{},{
????????????????????page,
????????????????????pageSize:pageSize,
????????????????????sort:{"add_time":-1}
?????????????????});?????
????????????????return?articleList;
????????????}
????????},
????}
})
//?scehma/default.js?
//增加?修改?刪除
//?定義根MutationRoot實現增刪改
var?MutationSchema=new?GraphQLObjectType({
????name:"mutation",
????fields:{
????????addNav:{
????????????type:NavSchema,
????????????args:{
????????????????title:?{type:?new?GraphQLNonNull(GraphQLString)},?????//表示title?和?url是必傳字段
????????????????url:?{type:?GraphQLNonNull(GraphQLString)},
????????????????sort:?{type:?GraphQLInt},
????????????????status:?{type:?GraphQLString},
????????????????add_time:?{type:?GraphQLString}
????????????},
????????????async?resolve(parent,?args)?{
????????????????var?result?=?await?DB.insert('nav',?{title:args.title,
????????????????????url:args.url,
????????????????????sort:args.sort,
????????????????????status:args.status,
????????????????????add_time:new?Date().getTime()
????????????????});
????????????????console.log(result.ops[0]);
????????????????return?result.ops[0];
????????????}
????????},
????????editNav:{
????????????type:NavSchema,
????????????args:{
????????????????_id:{type:?new?GraphQLNonNull(GraphQLString)},
????????????????title:?{type:?new?GraphQLNonNull(GraphQLString)},?????//表示title?和?url是必傳字段
????????????????url:?{type:?GraphQLNonNull(GraphQLString)},
????????????????sort:?{type:?GraphQLInt},
????????????????status:?{type:?GraphQLString},
????????????????add_time:?{type:?GraphQLString}
????????????},
????????????async?resolve(parent,?args)?{
????????????????var?result?=?await?DB.update('nav',?{"_id":DB.getObjectId(args._id)},{title:args.title,
????????????????????url:args.url,
????????????????????sort:args.sort,
????????????????????status:args.status,
????????????????????add_time:new?Date().getTime()
????????????????});
????????????????//?console.log(result);
????????????????return?{
????????????????????_id:args._id,
????????????????????title:args.title,
????????????????????url:args.url,
????????????????????sort:args.sort,
????????????????????status:args.status,
????????????????????add_time:new?Date().getTime()
????????????????}
????????????}
????????}????
????????,
????????deleteNav:{
????????????type:NavSchema,
????????????args:{
????????????????_id:{type:?new?GraphQLNonNull(GraphQLString)},
????????????},
????????????async?resolve(parent,?args)?{
????????????????var?oneNavList?=?await?DB.find('nav',?{?"_id":?DB.getObjectId(args._id)});
??????????????
????????????????var?deleteResult?=?await?DB.remove('nav',?{"_id":DB.getObjectId(args._id)});
????????????????console.log(deleteResult.result.n);
????????????????if(deleteResult.result.n){
????????????????????return?oneNavList[0];??
????????????????}else{
????????????????????return?{}
????????????????}
????????????}
????????}??
????}
})
//?掛載到GraphQLSchema
module.exports=new?GraphQLSchema({
????//?query:RootSchema,
????mutation:MutationSchema
})
可以看到必填字段不填會提示
再次查詢列表
ApolloBoost是一種零配置開始使用ApolloClient的方式。它包含一些實用的默認值,例如我們推薦的InMemoryCache和HttpLink,它非常適合用于快速啟動開發。將它與vue-apollo和graphql一起安裝:
npm?install?vue-apollo?graphql?apollo-boost?--save
src/main.js
中引入apollo-boost
模塊并實例化ApolloClient
import?ApolloClient?from'apollo-boost'
const?apolloClient?=?newApolloClient({
????//你需要在這里使用絕對路徑
????uri:'http://118.123.14.36:3002/graphql'
})
可以打開 http://118.123.14.36:3002/graphql 在控制臺查看查詢結果
src/main.js
配置vue-apollo
插件import?VueApollofrom'vue-apollo'
Vue.use(VueApollo);
Apollo provider
Provider保存了可以在接下來被所有子組件使用的Apollo客戶端實例
const?apolloProvider?=?newVueApollo({
????defaultClient:apolloClient
})
使用apollo Provider
選項將它添加到你的應用程序
new?Vue({
????el:'#app',
????apolloProvider,
????render:h=>h(App)
})
組件加載的時候就會去服務器請求數據,請求的數據會放在
navList
這個屬性上面,在模板中可以直接使用當前屬性
簡單查詢文檔
帶參數查詢參考
import?gql?from'graphql-tag';
export?default{?
????data(){
????????return?{?msg:?'我是一個?home?組件'?}?
????},
????apollo:?{
????????//?簡單的查詢,將更新?'hello'?這個?vue?屬性?
????????navList:?gql`query?{?
????????????navList?{?
????????????????title
????????????}?
????????}`?
????},
}
另一種寫法:
import?gql?from?'graphql-tag';?
export?default{?
????data(){
????????return?{?
????????????msg:'我是一個?home?組件'?
????????}?
????},
????//?Apollo?具體選項
????apollo:?{
????????//?//?帶參數的查詢
????????//?ping:?{
????????//?????//?gql?查詢
????????//?????query:?gql`query?PingMessage($message:?String!)?{
????????//?????ping(message:?$message)
????????//?????}`,
????????//?????//?靜態參數
????????//?????variables:?{
????????//?????message:?'Meow',
????????//?????},
????????//?},
????},
????apollo:?{?
????????//?注意方法名稱?和?查詢的名稱對應?
????????navList(){?
????????????return?{?
????????????????query:gql`query?{?
????????????????????navList?{?
????????????????????????title
?????????????????????}?
????????????????}`?
????????????}?
????????}?
????}?
}
完整例子
<template>
??<div?class="news">
????<h1>{{?msg?}}</h1>?
????<ul>
??????<li?v-for="(item,index)?of?navList"?:key="index">
??????????{{item.title}}
??????</li>??????
????</ul>
????<br>
????<hr>
????<br>
????<ul>
??????<li?v-for="(item,?index)?of?articleList"?:key="index">
??????????{{item.title}}---{{item.status}}--{{item._id}}
??????</li>??????
????</ul>
??</div>
</template>
<script>
??import?gql?from?'graphql-tag';
??export?default?{
????name:?'app',
????data(){
??????return{
????????msg:'我是一個首頁頁面'
??????}
????},
????apollo:?{
??????//?簡單的查詢,將更新?'hello'?這個?vue?屬性
??????navList:?gql`{
?????????navList{
????????????title
??????????}
??????}`,
??????articleList:gql`{
?????????articleList{
????????????title,
????????????status,
????????????_id
??????????}
??????}`
????}
?
??}
</script>
高級查詢文檔
??<div?class="news">
????<h1>{{?msg?}}</h1>????
????<ul>
??????<li?v-for="(item,key)?of?articleList"?v-bind:key="key">
??????????{{item.title}}---{{item.status}}
??????</li>??????
????</ul>
????<button?@click="getData()">
??????點擊按鈕觸發事件請求graphQl接口
????</button>
????{{navList}}
??</div>
邏輯
import?gql?from?'graphql-tag';
??var?navListGql=gql`{
????????navList{
????????????title???????????
????????}
???}`;
??export?default?{
????name:?'app',
????data(){
??????return{
????????msg:'我是一個新聞頁面',
????????navList:[]
??????}
????},
????apollo:{
??????//?articleList:gql`{
??????//????????articleList{
??????//?????????title,
??????//?????????status
??????//???????}
??????//?}`
????????//?把請求的數據賦值給articleList
????????articleList:{
??????????query:gql`query?articleList($page:Int!,$pageSize:Int!){
????????????????articleList(page:$page,pageSize:$pageSize){
??????????????????title,
??????????????????status
????????????????}
??????????}`,
??????????variables:{
????????????page:2,
????????????pageSize:10
??????????}
????????}
????},
????methods:{
??????getData(){
????????this.$apollo.addSmartQuery('navList',{??????????
????????????query:navListGql,
????????????result(response){
??????????????console.log(response);
????????????},error(err){
??????????????console.log(err);
????????????}
????????})
??????}
????}
??}
<template>
??<div?class="article">
????<h1>{{?msg?}}</h1>???
????<button?@click="getData()">獲取文章數據</button>
???<ul>
??????<li?v-for="(item,key)?of?articleList"?v-bind:key="key">
??????????{{item.title}}
??????</li>??????
????</ul>
??</div>
</template>
<script>
??import?gql?from?'graphql-tag';
??var?articleListGql=gql`query?articleList($page:Int!,$pageSize:Int!){
???????articleList(page:$page,pageSize:$pageSize){
????????title???????
??????}
??}`;
??export?default?{
????name:?'app',
????data(){
??????return{
????????msg:'article頁面',
????????articleList:[]
??????}
????},
????methods:{
??????getData(){
????????this.$apollo.addSmartQuery('articleList',{
??????????query:articleListGql,
??????????variables:{
????????????page:2,
????????????pageSize:8
??????????},
??????????result(response){
????????????console.log(response)
??????????},error(err){
????????????console.log(err)
??????????}
????????})
??????}
????}
??}
</script>
詳情文檔參考
服務器端接口
<template>
??<div?class="news">
????<h1>導航的增加修改刪除</h1>?
????<div?class="navForm">
????????導航名稱:<input?v-model="navJson.title"?type="text"?/>?<br><br>
????????導航鏈接:?<input?v-model="navJson.url"?type="text"?/><br><br>
????????<button?@click="doAdd()">提交數據</button>
????????<button?@click="doEdit()">修改數據</button>
????????<button?@click="doDele()">刪除數據</button>
????</div>
??</div>
</template>
<script>
??import?gql?from?'graphql-tag';
??var?navMutationAddGql=gql`mutation($title:String!,$url:String!){
????addNav(title:$title,url:$url){
??????title
????}
??}`;
??var?navMutationEditGql=gql`mutation($id:String!,$title:String!,$url:String!){
????editNav(_id:$id,title:$title,url:$url){
??????title
????}
??}`;
??var?navMutationDelGql=gql`mutation($id:String!){
????deleteNav(_id:$id){
??????title
????}
??}`;
??export?default?{
????name:?'app',
????data(){
??????return{
????????navJson:{
??????????title:"",
??????????url:""
????????}
??????}
????},
????methods:{
??????//?提交表單
??????doAdd(){
??????????//?eslint-disable-next-line?no-console
??????????console.log(this.navJson.title);
????????this.$apollo.mutate({
????????????mutation:navMutationAddGql,
????????????variables:?{
????????????title:?this.navJson.title,
????????????url:this.navJson.url,
????????????}
????????}).then((response)=>{
????????????console.log(response);
????????}).catch((err)=>{
????????????console.log(err);
????????})
??????},
??????//?修改數據
??????doEdit(){
????????this.$apollo.mutate({
??????????mutation:navMutationEditGql,
??????????variables:?{
????????????id:"62beaf16323cb708d06580ce",
????????????title:?this.navJson.title,
????????????url:this.navJson.url,
??????????}
????????}).then((response)=>{
??????????console.log(response);
????????}).catch((err)=>{
??????????console.log(err);
????????})
??????},
??????doDele(){
????????this.$apollo.mutate({
??????????mutation:navMutationDelGql,
??????????variables:?{
????????????id:"62beaf50323cb708d06580d0",
??????????}
????????}).then((response)=>{
??????????console.log(response);
????????}).catch((err)=>{
??????????console.log(err);
????????})
??????}
????}
?
??}
</script>
可以看到新增成功效果
項目例子完整代碼下載地址 https://blog.poetries.top/assets/graphql-code.zip
文章轉自微信公眾號@程序員poetry