EggJS接口开发
需求
随着nodejs的普及,前端开发的开发场景基本可以贯穿界面交互到数据存储,无缝实现全栈开发。最近在实现一个内部项目管理工具的时候,就尝试了一把接口和数据库开发。
什么是egg.js
egg.js是阿里开源的一套nodejs开发框架。egg.js官网的介绍是:
egg.js 为企业级框架和应用而生,我们希望由 egg.js 孕育出更多上层框架,帮助开发团队和开发人员降低开发和维护成本。
为什么选择了egg.js,而不是koa,express呢,其实还是为了快速开发,减少搭建项目的时间,egg.js已经为开发者设计了几乎最常用的目录结构,一切倾向于配置化,隐藏一些业务无关的技术细节。开发者可以更加着重考虑业务逻辑,然后在egg.js和相关插件的支持下,开发功能即可。
egg.js还提倡『约定优于配置』,这一点我也是很赞同,一致的约定能够减少不必要的失误,同时保证了一致的开发体验,可以方便的维护不同的项目。
初始化项目
egg.js提供了脚手架快速初始化项目,但是要求npm >=6.1.0,这基本不是问题。
$ mkdir egg-example && cd egg-example
$ npm init egg --type=simple
$ npm i
然后启动项目
$ npm run dev
$ open http://localhost:7001
目录设计
因为egg.js已经做了太多事情,我只需要关注app和config。
├── app
| ├── router.js
│ ├── controller
│ | └── home.js
│ ├── service
│ | └── user.js
│ ├── model
│ | └── user.js
├── config
| ├── plugin.js
| └── config.default.js
app/router.js 用于配置url路由规则,也就是你访问的接口地址,对应的是哪个controller的逻辑。
app/controller/** 用于解析用户的输入,处理后返回相应的结果,这里其实可以写service和model的逻辑,但是按照单一职责的原则,我们会在controller主要放置参数解析和返回值,以及非数据库操作的逻辑。
app/service/** 用于编写业务逻辑层,可选,建议使用,你可以理解成对数据库操作的封装。
app/model/** 用于定义mongodb的schema,这部分很神奇的是egg.js已经封装mongodb链接数据库,并将model绑定到了ctx上,方便调用。
config/config.default.js 用于编写配置文件,可以配置不同的开发环境,不同的变量,但是因为业务比较单一,内部使用,所以只使用了默认设置。我的项目中配置了跨域、mongoose、csrf,等等。
config.mongoose = {
url: "mongodb://127.0.0.1/*****",
options: {}
};
config.cors = {
origin: '*',
allowmethods: 'get,head,put,post,delete,patch'
}
config.security = {
csrf: {
enable: false,
},
};
config/plugin.js 用于配置需要加载的插件,比如egg-mongoose,egg-cors。
module.exports = {
mongoose: {
enable: true,
package: "egg-mongoose"
},
cors: {
enable: true,
package: "egg-cors"
}
};
这里需要注意的是,很多博客提供的代码都是es6的代码,在我初始化的模板中是不可行的,如下:
exports.cors = {
enable: true,
package: 'egg-cors',
}
开发项目
基础搭建好了,开发就变得很简单了,我遵循的逻辑是:model-->路由-->contoller-->service,先设计数据库schema,然后增加新的路由,支持对应的controller,然后在service中完成数据库操作。
router.js
router.get("/api/task", controller.task.index);
router.post("/api/task", controller.task.create);
router.put("/api/task/:id", controller.task.update);
router.delete("/api/task/:id", controller.task.destroy );
// 也可以简写为
router.resources('topics', '/api/task', controller.task);
controller中实现的方法具体可以参考下面的对应关系
method | path | route name | controller.action |
---|---|---|---|
get | /posts | posts | app.controllers.posts.index |
get | /posts/new | new_post | app.controllers.posts.new |
get | /posts/:id | post | app.controllers.posts.show |
get | /posts/:id/edit | edit_post | app.controllers.posts.edit |
post | /posts | posts | app.controllers.posts.create |
put | /posts/:id | post | app.controllers.posts.update |
delete | /posts/:id | post | app.controllers.posts.destroy |
controller/task.js
exports.index = function*() {
// ...
const result = yield this.service.task.index(this.params);
this.body = result;
};
exports.create = function*() {
// ...
const result = yield this.service.task.create(this.request.body);
this.body = result;
};
exports.update = function*() {
// ...
const result = yield this.service.task.update(this.params.id, this.request.body);
this.body = result;
};
exports.destroy = function*() {
// ...
const result = yield this.service.task.destroy(this.params);
this.body = result;
};
service/task.js
module.exports = app => {
class taskservice extends app.service {
*index(params) {
let tasks = yield this.ctx.model.task.find(params);
let result = {};
result.data = tasks;
return result;
}
*create(request) {
}
*update(id, request) {
}
*destroy(params) {
}
}
return taskservice;
};
model/task.js
module.exports = app => {
const mongoose = app.mongoose;
const schema = mongoose.schema;
const taskschema = new schema({
id: {type: number},
text: {type: string},
type: {type: string},
progress: {type: number},
open: {type: boolean},
start_date: {type: string},
owner_id: [{type: string}],
duration: {type: number},
parent: {type: number}
});
return mongoose.model("task", taskschema);
};
部署
egg.js 框架内置了 egg-cluster 来启动 master 进程,master 有足够的稳定性,不再需要使用 pm2 等进程守护模块。只需要两个命令即可:
# 启动服务
npm start
# 关闭服务
npm run stop
结语
站在巨人的肩膀上,让我们的开发效率倍增,但是还是建议大家先从koa2学起,对然后对比egg.js,你就会了解它到底封装了哪些东西,为我们节省了多少工作量,后面还要继续对egg.js的插件开发进行了解。