preface

I was recently working on a complete project and suddenly wanted to write the back-end interface in Node. I looked for information on the Internet and found that most of them were SSR. Few people wrote similar articles about how to build the directory structure of pure Node back-end projects. So try to use the traditional backend MVC mode here, combined with their own project summary of an article built from zero, if there is insufficient, also please big guy advice 🙏.

1. MVC pattern/directory initialization

You’re probably familiar with the MVC pattern, which is the Model-View-Controller pattern. Therefore, in this project:

  • Model: the data layer
  • Controller: control layer

The initialization structure of the project directory is defined as follows:

express
router
view
router.js

As the project also needs to connect to the database, libs and config directories are added to manage the connection and configuration of the database, static folder is added to store static resources, utils is added to store tool functions, and the final directory structure of the project is as follows (as the project logic is not very complex, temporarily decided, hope to get your correct) :

2,Model/Viewlayer

Since the View layer is only used for the management interface, it is relatively simple, and the specific logic is handled in the Controller layer

// view/router.js
const express = require('express');
const router = express.Router();
const controller = require('.. /controller'); / / query router. Get ('/login', controller.user.login);
Copy the code

For the initialization of the Model layer, you can define the connection of the database first. In order to facilitate the later configuration change, put the database configuration in the config separately:

// config/index.js
module.exports = {
    DB_HOST: 'localhost',
    DB_USER: 'root',
    DB_PASSWORD: '* * * * *',
    DB_PORT: '3306',
    DB_NAME: '* * * * *'
};
Copy the code

Then initialize the database connection under the LIBS

const mysql = require('mysql');
const DB_CONFIG = require('.. /config');
const co = require('co-mysql');

const pool = mysql.createPool({
    host: DB_CONFIG.DB_HOST,
    user: DB_CONFIG.DB_USER,
    password: DB_CONFIG.DB_PASSWORD,
    port: DB_CONFIG.DB_PORT,
    database: DB_CONFIG.DB_NAME,
});

module.exports = co(pool);
Copy the code

The Co-mysql library is used in conjunction with async await to handle node-mysql hell callbacks. The principle is very simple, just a few lines of code, you can go to GitHub if you are interested.

After the initialization of the database connection, write SQL statements respectively in the Model directory according to different requirements to deal with operations such as add, delete, change and check. Take the login interface as an example:

// model/login.js
const db = require('.. /libs/database');

module.exports = async (data) => {
    const { name, password, phone } = data;
    const searchKey = name ? 'user_name' : 'phone';
    const searchValue = name ? name : phone;
    const searchSQL = `SELECT * FROM user_info WHERE ${searchKey} = "${searchValue}" AND password = "${password}"`;
    return await db.query(searchSQL);
};
Copy the code

You can do a little bit of processing here, and then you can wrap a promise layer, and then you’re basically done with the Model layer and the View layer.

3,Controllerlayer

In Controller, you need to associate the Model layer with the View layer, manage the logic of different modules through a unified Modules directory, and finally expose a Controller object for the View layer to call.

// index.js
const user = require('./modules/user');
const home = require('./modules/home');
const items = require('./modules/items');
const order = require('./modules/order');

module.exports = {
    user,
    home,
    items,
    order
};
Copy the code

The function of such layering is that when dealing with the logic of different modules, only the controller with the corresponding function under the corresponding module can process the request. Different modules can also be called in the corresponding module if they are associated.

Here take the login and registration interface as an example, defined under the user object:

const resJson = require('.. /.. /utils/resJson');
const tokenUtils = require('.. /.. /utils/token');
const loginModel = require('.. /.. /model/login');
const registerModel = require('.. /.. /model/register');
const verifyModel = require('.. /.. /model/verifyRegister');
const md5 = require("md5"); Const user = {// login: async (req, res) => {res.writehead (200, {'Content-Type': 'text/html; charset=utf-8'});
        const {name, password, phone} = req.body;
        await loginModel({name, password: md5(password), phone}).then(data => {
            if (data.length) {
                const tokenData = {
                    name: data[0].user_name,
                    password: data[0].password,
                    phone: data[0].phone,
                    user_uid: data[0].user_uid
                };
                res.end(resJson.returnSuccess({token: tokenUtils.getToken(tokenData)}));
            } else {
                res.end(resJson.returnError(500, 'Wrong username or password'));
            }
        }).catch(e => {
            res.end(resJson.returnError(500, 'Wrong username or password')); }, // Register: async (req, res) => {let flag = true;
        res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'}); const {name, password, phone} = req.body; // verify that user name has await verifyModel('user_name', name).then(data => {
            if (data.length) {
                res.end(resJson.returnError(500, 'Username already exists'));
                flag = false;
            }
        }).catch(e => {
            res.end(resJson.returnError(500, e));
            flag = false;
        });
        if(! flag)return; // Verify that the phone number has await verifyModel('phone', phone).then(data => {
            if (data.length) {
                res.end(resJson.returnError(500, 'Mobile phone number already exists'));
                flag = false;
            }
        }).catch(e => {
            res.end(resJson.returnError(500, e));
            flag = false;
        });
        if(! flag)return;

        await registerModel(req.body).catch(e => {
            res.end(resJson.returnError(500, e));
            flag = false;
        });
        if(! flag)return;

        await loginModel({name, password: md5(password), phone}).then(data => {
            if (data.length) {
                const tokenData = {
                    name: data[0].user_name,
                    password: data[0].password,
                    phone: data[0].phone,
                    user_uid: data[0].user_uid
                };
                res.end(resJson.returnSuccess({token: tokenUtils.getToken(tokenData)}));
            } else {
                res.end(resJson.returnError(500, 'Wrong username or password'));
            }
        }).catch(e => {
            res.end(resJson.returnError(500, 'Wrong username or password')); })}}; module.exports = user;Copy the code

Finally, in router.js, you only need to call the login method in user to implement the interface function.

conclusion

The overall structure of the project designed in this mode is relatively clear, and the basic ideas are as follows:

  • The user initiates a request, and the Express-Router processes the request

  • The Node Server then parses the request and locates the controller to perform the specific operation.

  • Go to the Controller layer and perform an operation that requires manipulation of the database.

  • When entering the Model layer, the corresponding SQL statement of the Model layer is called.

  • The Controller layer then returns the data to the user.

Above is the whole process of building the project from scratch, if there are any shortcomings, please also point out!