在 Nest.js 的文档中看到了有集成 GraphQL 的指导,所以在本地尝试下先用 Koa 写出一个 DEMO,之后再去与 Nest.js 集成起来。
先写出数据库模型(这个文件是之前就存在的,没有做更改,将文件名改成了 models.ts):
/**
* Created by w on 2018/4/13.
*/
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/ticket', {
server: {
socketOptions: {keepAlive: 1}
}
});
const models = {
users: {
username: {
type: String,
required: true
},
password: {
type: String,
required: true
},
description: {type: String},
createTime: {
type: Date,
default: new Date()}
},
tickets: {
name: {
type: String,
required: true
},
price: {
type: Number,
requred: true
},
holdTime: { // 举办时间
type: Date,
required: true
},
count: { // 剩余数量
type: String,
required: true
},
place:{
type: String,
required: true
},
img: {type: String},
description: {type: String},
publicTime: {
type: Date,
default: new Date()},
}
};
for (let m in models) {mongoose.model(m, mongoose.Schema(models[m]));
}
module.exports = {getModules: (name) => {return mongoose.model(name);
}
};
之后编写各自模型的 GraphQL 查询文件(user.ts)):
// @ts-ignore
const {
//@ts-ignore
graphql, GraphQLSchema, GraphQLObjectType,GraphQLString,GraphQLID,GraphQLList,GraphQLNonNull,GraphQLInt,isOutputType,
} = require('graphql');
// @ts-ignore
const User = require('../db/model').getModules('users');
const userType = new GraphQLObjectType({
name: 'User',
fields: {
username: {type: GraphQLString,},
password: {type: GraphQLString,},
description: {type: GraphQLString,},
createTime: {type: GraphQLString,}
}
});
module.exports = {
query: {type: new GraphQLList(userType),
args: {
username: {
username: 'username',
type: GraphQLNonNull(GraphQLString)
}
},
resolve(root, params, options) {if (params.username === '$all') {return User.find().exec()}
return User.find({username: params.username}).exec();}
},
create: {type: new GraphQLList(userType),
args: {
username: {
username: 'username',
type: GraphQLNonNull(GraphQLString)
},
possword: {
name: 'price',
type: GraphQLNonNull(GraphQLString)
},
createTime: {type: GraphQLString,},
description: {
name: 'description',
type: GraphQLString
}
},
resolve: async (root, params, options) => {
let user = new User({
username: params.username,
password: params.password,
description: params.description,
createTime: params.createTime
});
try {await user.save();
return user.find({username: params.username}).exec()}catch(e) {console.error("Error while save data:", e);
}
}
}
}
ticket.ts:
// @ts-ignore
const {
// @ts-ignore
graphql, GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLID, GraphQLList, GraphQLNonNull, GraphQLInt, isOutputType,
} = require('graphql');
// @ts-ignore
const Ticket = require('../db/model').getModules('tickets');
const ticketType = new GraphQLObjectType({
name: 'Ticket',
fields: {
name: {type: GraphQLString},
price: {type: GraphQLInt},
holdTime: {type: GraphQLString},
count: {type: GraphQLInt},
place: {type: GraphQLString},
img: {type: GraphQLString},
description: {type: GraphQLString},
publicTime: {type: GraphQLString}
}
});
module.exports = {
query: {type: new GraphQLList(ticketType),
args: {
name: {
name: 'name',
type: GraphQLNonNull(GraphQLString)
}
},
resolve(root, params, options) {if (params.name === '$all') {return Ticket.find().exec()}
return Ticket.find({name: params.name}).exec();}
},
create: {type: new GraphQLList(ticketType),
args: {
name: {
name: 'name',
type: GraphQLNonNull(GraphQLString)
},
price: {
name: 'price',
type: GraphQLInt
},
holdTime: {
name: 'holdTime',
type: GraphQLNonNull(GraphQLString)
},
count: {
name: 'count',
type: GraphQLNonNull(GraphQLInt)
},
place: {
name: 'place',
type: GraphQLNonNull(GraphQLString)
},
img: {
name: 'img',
type: GraphQLString
},
description: {
name: 'description',
type: GraphQLString
},
publicTime: {
name: 'publicTime',
type: GraphQLString
}
},
resolve: async (root, params, options) => {
let ticket = new Ticket({
name: params.name,
price: params.price,
holdTime: params.holdTime,
count: params.count,
place: params.place,
img: params.img,
description: params.description,
publicTime: params.publicTime
});
try {await ticket.save();
return Ticket.find({name: params.name}).exec()}catch(e) {console.error("Error while save data:", e);
}
}
}
}
接下来编写用于查询或修改数据的 Schema.ts:
const {
//@ts-ignore
GraphQLSchema, GraphQLObjectType,
} = require('graphql');
//@ts-ignore
const Ticket = require('./ticket');
//@ts-ignore
const User = require('./user');
module.exports = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Queries',
fields: {
Ticket: Ticket.query,
User: User.query,
}
}),
mutation: new GraphQLObjectType({
name: 'Mutations',
fields: {
Ticket: Ticket.create,
User: User.create,
}
})
})
编写服务端 (server.ts):
const Koa = require('koa');
const app = new Koa();
const koaBody = require('koa-body');
const CombileRouter = require('./combineRouter');
app
.use(koaBody({
multipart: true,
formidable: {keepExtensions: true,},
}))
.use(CombileRouter.routes())
.use(CombileRouter.routes());
app.listen(5000, () => {console.log('App running~');
})
定义 ticket 路由的 ticket.router.ts:
const KoaRouter = require('koa-router');
const ticketRouter = new KoaRouter();
// @ts-ignore
const {graphqlKoa, graphiqlKoa} = require('graphql-server-koa');
// @ts-ignore
const Ticket = require('../db/model').getModules('tickets');
const ticketSchema = require('../graphql/schema');
ticketRouter.get('/ticket/all', async (ctx) => {
try {let result = await Ticket.find({});
ctx.body = {data: Object.assign({}, result), code: '0' };
} catch (e) {console.error("Getting all ticket error:", e);
}
});
ticketRouter.post('/graphql', async (ctx, next) => {console.log('graphql', ctx.request.body);
await graphqlKoa({schema: ticketSchema})(ctx, next)
})
.get('/graphql', async (ctx, next) => {await graphqlKoa({schema: ticketSchema})(ctx, next)
})
.get('/graphiql', async (ctx, next) => {await graphiqlKoa({endpointURL: '/graphql'})(ctx, next)
})
module.exports = ticketRouter;
还有之前定义的 user.router.ts(测试这个 user 路由是否是 reachable 的):
const Router = require('koa-router');
const userRouter = new Router();
// @ts-ignore
const {graphiqlKoa} = require('graphql-server-koa');
userRouter.get('/user/all', async (ctx) => {
ctx.body = {
code: '0',
msg: 'OK',
info: {
data: [{
username: 'a',
id: 0,
desc: 'ok',
}, {
username: 'd',
id: 1,
desc: 'ok',
}, {
username: 'c',
id: 2,
desc: 'ok',
}, {
username: 'b',
id: 3,
desc: 'ok',
}]
}
}
});
module.exports = userRouter;
将两个路由文件融合起来 (combineRouter.ts):
const UserRouter:object = require('./koa-router/user.router');
const TicketRouter:object = require('./koa-router/ticket.router');
const combiler = (routers: object[]): object => {let arr = [];
routers.forEach(v => {
//@ts-ignore
arr.push(...v.stack)
});
return Object.assign(routers[0], {stack:arr});
}
const res = combiler([UserRouter, TicketRouter]);
module.exports = res;
OK 了,这部分就写好了基础的 query 和 mutation 功能,还有删除的功能之后再加上。
附上 tsconfig.json 文件:
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"sourceMap": true,
"experimentalDecorators": true,
},
"exclude": ["node_modules"]
}