从零开始搭建一个Node的项目,使用Express框架,使用Sqlite作为数据库,废话不多说,直接开始
初始化项目
- 创建空文件,然后init项目
 
mkdir node-project
cd node-project
npm init
创建项目目录
|—— common/       //公共模块 
  |—— authenticate.js //认证管理
  |—— database.js     //数据库连接池
  |—— webSocket.js    //websocket服务
|—— Interface/    //接口
  |—— user.js         //用户管理接口
  |—— ...
|—— Logs/         //日志
  |—— info.log      //详细日志文件
  |—— error.log     //错误日志文件
  |—— ...
|—— Routes/       //路由
|—— SQLiteStudio/ //数据库管理工具(忽略该文件夹)
|—— .env          //环境变量
|—— .gitignore    //忽略文件
|—— index.js      //入口文件
|—— package.json  //项目配置文件
|—— README.md     //项目说明
|—— SQLite.db     //数据库文件
|—— SQLite.sql    //数据库脚本
|—— swaggerConfig.js //swagger配置文件
安装相关模块
npm install nodemon –save
npm install dotenv –save
npm install express –save
npm install sqlite3 –save
npm install generic-pool –save
npm install log4js –save
npm install jsonwebtoken –save
npm install express-jsdoc-swagger –save
npm install swagger-jsdoc –save
npm install express-ws –save
npm install cors –save
npm install body-parser –save
热更新启动
安装 nodemon 模块,在package.json中添加启动命令。
npm install nodemon –save
// package.json
"scripts": {
  "dev": "nodemon index.js"
}
环境变量
安装 dotenv 模块,创建.env 文件,并设置环境变量。
npm install dotenv –save
// .env
PORT = 8888
SECRET_KEY = "123456"
Express
Express 是一个用于 Node.js 的快速构建的 Web 框架。
npm install express –save
// index.js
require('dotenv').config();
const express = require('express');
const app = express();
const PORT = process.env.PORT || 8888;
// 启动 Express 服务 
const c  = require('child_process')
const server=app.listen(PORT,()=>{
  console.log('  Express 服务启动,正在监听'+ PORT +'端口');
  c.exec('start http://localhost:8888/docs')
})
数据库支持
创建数据库文件,设置连接池,创建连接池管理文件。
npm install sqlite3 –save
npm install generic-pool –save
// 跳过预编译,直接源码编译
npm install –build-from-source
// common/database.js
const sqlite3 = require('sqlite3').verbose(); // 引入sqlite3模块
const genericPool = require('generic-pool');  // 引入generic-pool模块
const factory = {
  create: function() { // 创建一个数据库实例
    return new Promise(function(resolve, reject) {
      let db = new sqlite3.Database('./SQLite.db', sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => { // 创建数据库实例
        if (err) {
          reject(err);
        } else {
          resolve(db);
        }
      });
    });
  },
  destroy: function(db) { // 销毁一个数据库实例
    return new Promise(function(resolve) {
      db.close();
      resolve();
    });
  }
};
const opts = {
  max: 10, // 最大连接数
  min: 2  // 最小连接数
};
const myPool = genericPool.createPool(factory, opts); // 创建连接池
module.exports = myPool; // 导出连接池
日志管理
你如果要使用日志管理文件,可以安装以下两大模块之一:
- Log4js 提供了模块化的日志记录功能,可以根据需要为不同的模块或功能分配独立的日志级别,从而实现更精细的信息输出控制。
npm install log4js –save
 
// common/log.js
const log4js = require('log4js');
log4js.configure({
  appenders: {
    errorFile: { type: 'file', filename: '../Logs/errors.log' },
    debugFile: { type: 'file', filename: '../Logs/debugs.log' },
    infoFile: { type: 'file', filename: '../Logs/info.log' },
    error: { type: 'logLevelFilter', level: 'error', appender: 'errorFile' },
    debug: { type: 'logLevelFilter', level: 'debug', appender: 'debugFile', maxLevel: 'debug' },
    info: { type: 'logLevelFilter', level: 'info', appender: 'infoFile', maxLevel: 'info' }
  },
  categories: {
    default: { appenders: ['info', 'debug', 'error'], level: 'trace' }
  }
});
const logger = log4js.getLogger();
module.exports = logger;
- winston是一个日志记录器,它提供了灵活的配置和灵活的输出功能。winston-daily-rotate-file 是一个基于winston的日志模块,它允许你根据日期自动生成日志文件。
 
npm install winston –save
npm install winston-daily-rotate-file –save
身份校验
JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。这种信息可以被验证和信任,因为它是数字签名的。JWT可以使用密钥(例如,秘密或公钥/私钥对)进行签名,也可以选择性地加密其包含的信息
npm install jsonwebtoken –save
// common/authenticate.js
const jwt = require('jsonwebtoken');
const SECRET_KEY = process.env.SECRET_KEY; // 密钥
exports.authenticateToken=(req, res, next)=>{
    const authHeader = req.headers['authorization']; // 获取请求头中的token
    const token = authHeader && authHeader.split(' ')[1];
    if (token == null){
        return res.status(401).send({status:"401",message:'无权限访问'})
    } 
    jwt.verify(token, SECRET_KEY, (err, user) => {
        if (err){
            return res.status(403).send({status:"403",message:'token无效或已过期'})
        }
/* //临近过期时(前10分钟),刷新token
        let Expiration = process.env.Expiration
        let exp = user.exp * 1000
        let now = new Date().getTime()
        console.log(new Date(exp).toLocaleString(),now - exp)
        if (exp - now <= 60 * 1000) {
            const newToken = jwt.sign({ id: user.UserID, name:user.UserName }, SECRET_KEY, { expiresIn: Expiration });
            res.setHeader('Refresh-Authorization',newToken );
        }
 */
        req.user = user; // 将用户信息存储在请求中
        next(); // 继续执行后续操作
    });
}
// ../Interface/user
const myPool = require('../common/database.js');
//创建用户,以下的业务代码省略
exports.userCreatePost = async(req,res)=>{
  //...
}
// ./Routes/user.js
const express = require('express');
const router = express.Router();
const User = require('../Interface/user')
const {authenticateToken} = require('../common/authenticate')
// 创建用户
router.post('/api/user/create',authenticateToken,(req,res)=>{
  User.userCreatePost(req,res)
})
接口文档支持
如果需要使用接口文档,可以使用以下两个方案中的一个:
- express-jsdoc-swagger 能够根据JSDoc注释自动生成接口文档,并且支持 Swagger UI。介绍更详细,对前端更友好,但是需要自己编写注释。
 
npm install express-jsdoc-swagger –save
npm install swagger-jsdoc –save
// swaggerConfig.js
const options = {
  info: {
    version: '1.0.0',
    title: "node-template",
    description:"node-template's interface documentation",
    license: {
      name: 'MIT',
    },
  },
  security: {
    BasicAuth: {
      type: 'http',
      scheme: 'basic',
    },
  },
  baseDir: __dirname,
  filesPattern: './**/*.js',
  swaggerUIPath: '/docs',
  exposeSwaggerUI: true,
  exposeApiDocs: false,
  apiDocsPath: '/v3/api-docs',
  notRequiredAsNullable: false,
  swaggerUiOptions: {},
  multiple: true,
};
module.exports = options;
// index.js
const expressJSDocSwagger = require('express-jsdoc-swagger');
const swaggerConfig = require('./swaggerConfig.js')
expressJSDocSwagger(app)(swaggerConfig);
// ./Routes/user.js
/**
 * 创建用户的请求对象
 * @typedef {object} Register
 * @property {string} UserName.required - 用户名称
 * @property {string} Email.required - 用户邮箱
 */
/**
 * POST /api/user/create
 * @summary 用户的创建接口
 * @tags user
 * @param {Register} request.body - 用户信息 - application/json
 * @return {object} 200 - 注册成功 - application/json
 * @return 409 - 用户名已存在
 * @return 500 - 错误的查询
 * @example response - 200 - 注册成功示例 - application/json
 * {
 *     "id": "XXXXXXXXX",
 *     "UserName": "XXXXXXXXXX",
 *     "status": "200",
 *     "message": "用户注册成功"
 * }
 */
router.post('/api/user/create',authenticateToken,(req,res)=>{
  User.userCreatePost(req,res)
})
- swagger-ui-express 是一个基于express的WebSocket中间件,它允许你轻松地使用WebSocket协议。能根据json文件自动生成接口文档,并且支持 Swagger UI。对后端更轻松,但是文档不好看(我不喜欢这种,不仔细介绍了)。
 
npm install swagger-ui-express –save
npm install swagger-jsdoc –save
// swagger.json
{  
  "openapi": "3.0.3",  
  "info": {  
    "title": "My API",  
    "version": "1.0.0"  
  },  
  "paths": { //各个接口及相关的内容
    "/users": {  
      "get": { /*...*/ },  
      "post": { /*...*/ }  
    }  
  },  
  "components": { //请求对象和响应对象
    "schemas": {  
      "User": { /*...*/ }  
    }  
  }  
}
// index.js
const swaggerUi = require('swagger-ui-express');
const swaggerDocument = require('./swagger.json');
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
WebSocket
你如果要使用WebSocket,可以安装以下两个模块之一:
- ws是一个Node.js模块,主要用于在服务器端实现WebSocket协议。它允许客户端(通常是浏览器)与服务端建立持久化的连接.
 
npm install ws –save
// ./common/webSocket.js
const WebSocket = require('ws');
const jwt = require('jsonwebtoken');
const SECRET_KEY = process.env.SECRET_KEY; // 密钥
function verifyToken(token) { //验证token  
    var isValid = false
    var user = null;
    jwt.verify(token, SECRET_KEY, (err, _user) => {
        if (!err){
            isValid = true
            user = _user
        }
    });
    return {isValid,user};  
}
class WebSocketServer {  
    constructor(port) {  
        this.wss = new WebSocket.Server({ 
            port,
            verifyClient: (info, done) => {
                const token = info.req.headers.authorization || info.req.url.split('?')[1].split('=')[1];
                const { isValid, user } = verifyToken(token);
                if (isValid) {  
                    info.req.user = user; //将用户信息附加到请求对象上
                    done(true); //验证通过,允许连接  
                  } else {  
                    done(false, 401, 'Unauthorized'); //验证失败,拒绝连接并返回 401 状态码  
                  }  
            }
         }); 
        this.clients = new Map(); //使用Map来存储客户端连接  
        this.wss.on('connection', this.onConnection.bind(this));  
    }
    //监听客户端连接
    onConnection(ws,req) {
        const user = req.user
        this.clients.set(user.id, ws); 
        console.log("WebSocket :"+user.id+"用户已上线");
        logger.info("WebSocket :"+user.id+"用户已上线")
        ws.on('message', (messageBuffer) => {  
            //将Buffer解码为UTF-8字符串  
            const messageString = messageBuffer.toString('utf8');
            console.log('接受消息:', messageString);  
            ws.send('Message received: ' + messageString); 
            
        });
        //断开连接
        ws.on('close', () => {
            this.removeClient(ws)
        });
    }
    //广播消息给所有连接的客户端,除了指定的ws  
    broadcast(message, exceptWs){
        this.clients.forEach((client) => {  
            if (client !== exceptWs && client.readyState === WebSocket.OPEN) {  
                client.send(message);  
            }
        });  
    }
    //推送给特定人员
    sendMessageToUser(userId, message) {
        const ws = this.clients.get(userId);
        if (ws && ws.readyState === WebSocket.OPEN) {
            ws.send(message);
        }else{
            console.log("WebSocket :"+userId+"用户不在线");
            logger.info("WebSocket :"+userId+"用户不在线")
        }
    }
    //从连接列表中移除指定的客户端  
    removeClient(userId) {  
        const ws = this.clients.get(userId);
        if (ws) {
            ws.close();
            this.clients.delete(userId);
            console.log("WebSocket :"+userId+"用户已下线");
            logger.info("WebSocket :"+userId+"用户已下线")
        }
    }  
    //启动服务
    start() { 
        console.log('WebSocket 服务启动,正在监听'+ this.wss.options.port +'端口');
        logger.info('./common/webSocket.js WebSocket服务启动,正在监听'+ this.wss.options.port +'端口');
    }  
}  
const WSPORT = process.env.WSPORT || 8889;
const WSdev = new WebSocketServer(WSPORT);
module.exports = WSdev;
// ./index.js
const WSdev = require('./common/webSocket');
WSdev.start(); //启动 WebSocket 服务
- express-ws是一个针对Node.js开发者的库,它扩展了流行的Express框架,通过添加对WebSocket协议的支持,让开发者能够利用熟悉的Express API轻松地创建和管理WebSocket服务器。(推荐使用)
 
npm install express-ws –save
中间件数据处理
npm install cors –save
npm install body-parser –save
// index.js
const app = express()
const cors = require('cors')
app.use(cors()) //允许跨域:启动所有Cors请求
const bodyParser = require('body-parser')
app.use(bodyParser.json());  //解析json数据
app.use(bodyParser.urlencoded({extended: false}));  //解析form数据
后续扩展
npm install express-session –save
npm install express-jwt –save
npm install express-validator –save
npm install express-winston –save
npm install express-rate-limit –save
npm install express-ipfilter –save
npm install express-ipware –save
npm install express-fileupload –save
npm install express-flash –save
npm install express-handlebars –save
npm install express-graphql –save
npm install express-graphql-auth –save
npm install express-graphql-auth-jwt –save