preface

Node.js is definitely a milestone for the front end, and with its growing popularity, it’s not just a plus, it’s a must for front end designers. Express has become the most popular framework for Node.js due to its fast, open and minimalist features, so it is a good and reliable choice for web service development. However, express is not immediately an out-of-the-box, full-featured Web server project. For example: Logging, error capture, mysql connectivity, token authentication, WebSocket, and many other common features require developers to install plug-ins to complete the configuration, which can be a very resource-consuming task if you are not familiar with Web server development or the Express framework. In this article, a series of common functions such as logging, error capture, mysql connection, token authentication and Websocket are configured on the initial express architecture. The project of this article has been open source on Github for you to learn and use in practical projects, hoping to reduce your workload. Get things done more efficiently and have more time to improve.

Hard to arrange for a long time, but also hope to manually thumbs up to encourage ~

The blog is on github: github.com/fengshi123/… , summarized all the author’s blog, also welcome to follow and star ~

The github address for this project is github.com/fengshi123/…

I. Project structure

1.1 basic Environment

This express server project has the following runtime environment configuration:

names The version number
node.js 11.12.0
npm 6.7.0
express 4.16.0
mysql 5.7

1.2. Run the project

(1) To install the plug-in, go to the express_project directory and run the following command:

npm install
Copy the code

(2) Start the development environment, go to the express_project directory, and run the following command:

npm run dev
Copy the code

(3) Start the official environment, go to the express_project directory, and run the following command:

npm run start
Copy the code

1.3. Project Directory

The project directory structure is as follows:

├─ bin Database initialization script │ ├─ db Project database installation, where setup.sh command entry, Initializing the database ├─ Common Methods, constant directory ├─ conf Basic configuration directory of the database ├─ main logical directory of the dao code ├─ log Log directory public Static file directory ├─ routes URL Route configuration ├─ .eslintrc.js EsLint Configuration Active.js Express.Active.json Project Dependency Configuration active.readme.md Project DescriptionCopy the code

Two, common functions

2.1 Combined with mysql

2.1.1 Install and configure mysql

For details, see the configuration document. You are advised to install navicat for mysql database management tool. It is usually used to check the addition, deletion, modification and check of database data.

2.1.2. Initialize the database

In the bin/db directory, we have configured and written the database initialization script, in which setup.sh is the written command entry, used to connect to the database, create a database, etc., the main logic has been commented as follows:

mysql -uroot -p123456 --default-character-set=utf8 <<EOF // You need to change the account password to your own
drop database if exists research; // Delete the database
create database research character set utf8; // Create a database
use research; // Switch to the research database and change the corresponding to your own
source init.sql; // Initialize the SQL table
EOF
cmd /k
Copy the code

We can insert the initial data for each table in init. SQL. The code is as follows, and the simple logic of how to insert the table data is not described here.

source t_user.sql;
source t_exam.sql;
source t_video.sql;
source t_app_version.sql;
source t_system.sql;
Copy the code

2.1.3 Database configuration

Conf /db.js/mysql /db.js/mysql /db.js/mysql /db.js/mysql /db.js/mysql /db.js/mysql /db.js

module.exports = {
  mysql: {
    host: '127.0.0.1'.user: 'root'.password: '123456'.database: 'research'.port: 3306}};Copy the code

2.1.4 Database connection and operation

In the dao/ userdao.js file, you can add, delete, modify, and query the data. You can copy the configured files and logic.

var conf = require('.. /conf/db'); // Import the database configuration
var pool = mysql.createPool(conf.mysql); // Use connection pooling

  add: function (req, res, next) {
    pool.getConnection(function (err, connection) {
      if (err) {
        logger.error(err);
        return;
      }
      var param = req.body;
      // Establish a connection to insert values into the table
      connection.query(sql.insert, [param.uid, param.name, param.password, param.role,param.sex], function (err, result) {
        if (err) {
          logger.error(err);
        } else {
          result = {
            code: 0.msg: 'Increase success'
          };
        }
        // Return the result of the operation to the front page in json form
        common.jsonWrite(res, result);
        // Release the connection
        connection.release();
      });
    });
  },
Copy the code

2.2 Log function

2.2.1 Morgan logs requests

Morgan is express’s default logging middleware and can be used as a node.js logging component without Express. For details of Morgan’s API, refer to Morgan’s Github repository. Here is the main introduction of our project, to help you with what configuration, what functions to achieve. The project is configured as follows in the app.js file:

const logger = require('morgan');
// Output logs to the directory
var accessLogStream = fs.createWriteStream(path.join(__dirname, '/log/request.log'), { flags: 'a'.encoding: 'utf8' }); 
app.use(logger('combined', { stream: accessLogStream }));
Copy the code

We have configured the above request logging so that each HTTP request is logged to the log/request.log file.

2.2.2, Winston Records error logs

Since Morgan can only log HTTP requests, we need Winston to log anything else we want to log, such as database access errors, etc. Winston is one of the most popular log libraries on Node.js. It is designed as a simple general-purpose log library that supports multiple transports (a transports is really a storage device, such as where the logs are stored). Each Logger instance in Winston can have multiple transport configurations at different logging levels; Of course it can also record request records. The details of Winston’s API can be found in Winston’s Github repository, but we won’t go into details here.

Here is the main introduction of our project, we use Winston to help you with the configuration, what functions. The project is configured as follows in the common/logger.js file:

var { createLogger, format, transports } = require('winston');
var { combine, timestamp, printf } = format;
var path = require('path');

var myFormat = printf(({ level, message, label, timestamp }) = > {
  return `${timestamp} ${level}: ${message}`;
});

var logger = createLogger({
  level: 'error'.format: combine(
    timestamp(),
    myFormat
  ),
  transports: [
    new (transports.Console)(),
    new (transports.File)({
      filename: path.join(__dirname, '.. /log/error.log')]}}));module.exports = logger;
Copy the code

With the logger.js file configuration above, we just need to import the file where needed, and then call logger.error(err) for the corresponding level of logging, the related error log will be recorded in the file log/error.log.

2.3 Request routing processing

In the project, we divided the request processing by module. We created the route configuration of each module in the Routes directory, for example, the user module, which is the user.js file. We introduced the route of each module in the main entry file of app.js, taking the user module as an example.

// Import user module route configuration
var usersRouter = require('./routes/users');
// Use the routing configuration of the user module
app.use('/users', usersRouter);
Copy the code

In the routes/users.js file, the route configuration is as follows:

var express = require('express');
var router = express.Router();

// Add users
router.post('/addUser'.function (req, res, next) {
  userDao.add(req, res, next);
});
// Get all users
router.get('/queryAll'.function (req, res, next) {
  userDao.queryAll(req, res, next);
});
// Delete the user
router.post('/deleteUser'.function (req, res, next) { userDao.delete(req, res, next); }); .Copy the code

Based on the above configuration, the request path starting with /users will be forwarded to the user module route for processing. Suppose the request path is /users/addUser, the logical processing of adding users will be performed. In this case, if you add a new module, you only need to add a new module route file according to the original specification to configure routes. This does not affect the original module route configuration and does not cause route conflicts.

2.4 Token authentication

Express-jwt is a middleware in Node.js that validates the validity of the JsonWebTokens specified in the HTTP request. If so, it sets the value of JsonWebTokens to req.user and routes them to the appropriate router. This module allows you to validate HTTP requests using the JWT token in your Node.js application. Express-jwt’s specific API can be found in the Express-JWT Github library, which will not be covered in detail here.

Here is the main introduction of our project, we use Express-JWT to help you with configuration, what functions. The project performs the following token interception configuration in the app.js file. If the token is not authenticated, a 401 authentication failure is returned to the client.

var expressJWT = require('express-jwt'); Use (expressJWT({secret: constant.secret_key}).use(expressJWT({secret: constant.secret_key}).use(expressJWT({secret: constant.secret_key}). ['/getToken', '/getToken/adminLogin', '/appVersion/upload', '/appVersion/download', /^\/public\/.*/, /^\/static\/.*/, /^\/user_disk\/.*/, /^\/user_video\/.*/ ] }));Copy the code

We can choose to inject the corresponding login user ID into the token when the token is generated, such as the configuration in the dao/ tokendao.js file.

// The UID is injected into the token
ret = {
  code: 0.data: {
	token: jsonWebToken.sign({
	  uid: obj.uid
	}, CONSTANT.SECRET_KEY, {
	  expiresIn: 60 * 60 * 24})}};Copy the code

Later, we can obtain the user ID information in the previously injected token through the request information req.body.uid in the request.

2.5. Cross-domain Configuration

We have carried out cross-domain configuration of the project in app.js, so that some clients, such as single-page application development and mobile applications, can access the corresponding interface of the server side across domains. We do the related cross-domain configuration in the app.js file:

app.all(The '*'.function (req, res, next) {
  res.header('Access-Control-Allow-Origin'.The '*');
  res.header('Access-Control-Allow-Headers'.The '*');
  res.header('Access-Control-Allow-Methods'.The '*');
  next();
});
Copy the code

2.6 Static Directory configuration

We configured a static directory in the project to provide static resource files (images, CSS files, JS files, etc.) services; Pass a directory containing static resources to express.static middleware to provide static resources. Images, CSS files, and javascript files in the public directory are provided below, but you can also create your own static resource directory with a similar configuration.

app.use('/', express.static(path.join(__dirname, 'public')));
Copy the code

2.7 Exception error handling

If we do not handle the server program, when the server code throws an exception, the node process will exit, so that users cannot access the server normally, causing serious problems. We configured the project to use the domain module to catch exceptions thrown by the server program. The main APIS for domain are domain. Run and error events. Simply put, exceptions raised by a function executed by domain.run can be caught by the Domain’s error event. The code we configure to use domain in our project is as follows:

var domain = require('domain');
// Handle an exception that is not caught, causing the node to exit
app.use(function (req, res, next) {  
  var reqDomain = domain.create();  
  reqDomain.on('error'.function (err) {   
     res.status(err.status || 500);    
     res.render('error'); 
  }); 
  reqDomain.run(next);
});
Copy the code

2.8. Restart automatically

Every time we modify the JS file, we need to restart the server, so that the modified content will take effect, but each restart is troublesome, which affects the development effect; So we introduced the Nodemon plugin into the development environment to enable real-time hot updates and automatic restarts of the project. So as described in section 1.3, we should use the NPM run dev command to start the project in the development environment, because we have configured the following commands in the package.json file:

  "scripts": {
    "dev": "nodemon ./app",},Copy the code

2.9 Use of PM2

Pm2 is a node process management tool that can simplify the cumbersome tasks of managing many node applications, such as performance monitoring, automatic restart, and load balancing, and is very simple to use. So we can use pM2 to start our server program. Here we do not cover pM2 in detail, if you are not familiar with the students can check this document.

2.10 File upload processing

Server applications inevitably deal with file upload operations, we have configured multiparty plug-in in the project, and provide related upload, rename and other operations, related code logic and comments in dao/common.js. If you are not familiar with multiparty plug-ins, you can refer to the Multiparty plug-in github library, we will not do a detailed introduction here.

var upload = function (path, req, res, next) {
  return new Promise(function (resolve, reject) {
    // Parse a file to upload
    var form = new multiparty.Form();
    // Set edit
    form.encoding = 'utf-8';
    // Set the file storage path
    form.uploadDir = path;
    // Set the single file size limit
    form.maxFilesSize = 2000 * 1024 * 1024;
    var textObj = {};
    var imgObj = {};
    form.parse(req, function (err, fields, files) {
      if (err) {
        console.log(err);
      }
      Object.keys(fields).forEach(function (name) { / / text
        textObj[name] = fields[name];
      });
      Object.keys(files).forEach(function (name) {
        if (files[name] && files[name][0] && files[name][0].originalFilename) {
          imgObj[name] = files[name];
          var newPath = unescape(path + '/' + files[name][0].originalFilename);
          var num = 1;
          var suffix = newPath.split('. ').pop();
          var lastIndex = newPath.lastIndexOf('. ');
          var tmpname = newPath.substring(0, lastIndex);
          while (fs.existsSync(newPath)) {
            newPath = tmpname + '_' + num + '. ' + suffix;
            num++;
          }
          fs.renameSync(files[name][0].path, newPath);
          imgObj[name][0].path = newPath; }}); resolve([imgObj, textObj]) }); }); };Copy the code

2.11 File/Directory Operations

The server program operates on server directories and files with high frequency. Compared with the default FS file operation module of Node. js, we have configured fS-extra plug-in in the project to operate server directories and files. Fs-extra module is an extension of fs module of the system. Provides more convenient API, and inherits the FS module API; For example, we use the mkdir method to create a file directory, the ensureDir method to confirm whether the directory exists, the remove method to delete files, the copy method to copy files, and so on. You can see the fs-extra rich operation methods in the dao/ filesdao.js file. If you haven’t touched fs-Extra yet, you can check out its Github library for more details.

2.12. Configure ESLint code checking

To keep the project code style consistent, we add ESLint to the project to check the JS code specification; You can configure in the.eslintignore file which files you don’t want to check with ESLint; You can also configure your team’s code style in.eslintrc.js.

Third, summary

This article describes the author’s open source, out of the box, various features related to the web server project, such as: Mysql integration, logging, error capture, token authentication, cross-domain configuration, automatic restart, file upload processing, ESLint configuration, and many other common features.

Hard to arrange for a long time, but also hope to manually thumbs up to encourage ~

The blog is on github: github.com/fengshi123/… , summarized all the author’s blog, also welcome to follow and star ~

The github address for this project is github.com/fengshi123/…