demand

With the popularity of Nodejs, the development scenario of front-end development can basically run through the interface interaction to the data storage, realizing the seamless full-stack development. I recently experimented with interface and database development while implementing an internal project management tool.

What is an Egg. Js

Egg.js is an open source Nodejs development framework of Ali. The egg.js website says:

Egg.js was born for enterprise-level frameworks and applications, and we hope that more upper-layer frameworks will emerge from egg.js to help development teams and developers reduce development and maintenance costs.

Why have you chosen egg.js over Koa Express? In order to speed up development and reduce setup time, egg.js has already designed almost the most common directory structure for developers, which tends to be configurable and hides some non-business technical details. Developers can focus more on business logic and then develop functionality with the support of egg.js and related plug-ins.

Egg.js also advocates “convention over configuration”, which I agree with. Consistent conventions reduce unnecessary errors and ensure a consistent development experience, making it easy to maintain different projects.

Initialize the project

Egg.js provides scaffolding for quick initialization projects, but requires NPM >=6.1.0, which is basically not a problem.

$ mkdir egg-example && cd egg-example
$ npm init egg --type=simple
$ npm i
Copy the code

And then start the project

$ npm run dev
$ open http://localhost:7001
Copy the code

Catalog design

Since egg.js already does so much, I just need to focus on app and config.

├ ─ ─ app | ├ ─ ─ the router. The js │ ├ ─ ─ controller │ | └ ─ ─ home. Js │ ├ ─ ─ service │ | └ ─ ─ the user. The js │ ├ ─ ─ model │ | └ ─ ─ the user. The js ├ ─ ─ The config | ├ ─ ─ plugin. Js | └ ─ ─ config. Default. JsCopy the code

App /router.js is used to configure the URL routing rules, that is, the address of the interface you visit, which controller is the logic corresponding to it.

App /controller/** is used to parse user input and return corresponding results after processing. We can actually write the logic of service and model here, but according to the principle of single responsibility, we will put the logic of parameter parsing and return values, as well as non-database operation in controller.

App /service/** for writing the business logic layer, optional, recommended, you can understand the encapsulation of database operations.

App /model/** is used to define the mongodb schema. The magic part of this is that egg.js already encapsulates the mongodb linked database and binds model to CTX for easy call.

The config/config.default.js file is used to write configuration files. You can configure different variables in different development environments. However, only the default Settings are used because services are simple and are used internally. My project is configured with cross-domain, MonGOOSE, CSRF, and so on.

config.mongoose = {
    url: "Mongo: / / 127.0.0.1 / * * * * *".options: {}}; config.cors = {origin: The '*'.allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH'
}

config.security = {
    csrf: {
        enable: false,}};Copy the code

Config /plugin.js is used to configure plug-ins that need to be loaded, such as egg-Mongoose, egg-cors.

module.exports = { 
    mongoose: {
        enable: true.package: "egg-mongoose"
    },
    cors: {
        enable: true.package: "egg-cors"}};Copy the code

Note here that many blogs provide ES6 code that is not available in the template I initialized, as follows:

exports.cors = {
  enable: true.package: 'egg-cors',}Copy the code

Development projects

Once the foundation is set up, the development becomes very simple. The logic I follow is: Model–> Route –>Contoller–>Service, first design the database Schema, then add new routes, support the corresponding Controller, and then complete the database operation in 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 ); // You can also abbreviate router. Resources ('topics'.'/api/task', controller.task);
Copy the code

For the methods implemented in controller, please refer to the corresponding relationship below

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; 
};
Copy the code

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;
};
Copy the code

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);
};
Copy the code

The deployment of

Egg-cluster is built into the egg.js framework to start the Master process, which is stable enough to eliminate the need for daemon modules such as PM2. Just two commands are needed:

# start service
npm start
# service shutdown
npm run stop
Copy the code

conclusion

Standing on the shoulders of giants will make our development more efficient, but it is advisable to start with Koa2. Yes, and then compare it with egg.js to see what it encapsulates and how much work it saves.