• 基于nodejs+express+knex+mysql搭建一个后台服务


    前言

    首先,我们对nodejs、express、knex、mysql进行说明:

    1. Node.js:Node.js 是一个开源的、跨平台的 JavaScript 运行时环境。
    2. express:Node.js web application framework 基于nodejs的web应用框架
    3. Knex:SQL Query Builder for Javascript 适用于 Javascript 的 SQL 查询生成器
    4. MySQL:关系型数据库

    可见,者就是一个使用JavaScript搭建的后台系统。实现总共包括以下几步:

    1. 安装依赖
    2. 编写启动文件
    3. 封装kenx类进行数据库操作
    4. 编写接口

    按照以上步骤,我们对每一个过程进行介绍:

    1. 安装依赖

    基于该技术栈搭建一个基础的后台系统,需要安装的依赖有:

    npm i ts-node --save
    npm i ts-loader --save
    npm i typescript --save
    npm i express --save
    npmp i knex --save
    npm i mysql2 --save
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    安装这些依赖之前,首先要确定正确下载安装nodejs,并配置环境变量。查看是否具有Nodejs的命令为:node --version

    1. 安装typescript ts-nodets-loader是因为本实例中我们使用的是typescript编写的。您也可以不使用typescript,就不需要安装上述三个依赖;
    2. mysql2是用来链接数据库的依赖包。据本次项目的经验,使用knex时,如果使用mysql客户端,在部署到服务器的容器中会出现客户端版本不兼容的报错,所以我们选择了使用 mysql2。您也可以根据自己的需要和安装部署的基本条件,选择使用mysql客户端。

    2.编写服务启动文件

    启动文件是用于服务启动的入口,包括监听服务启动端口、注入路由(接口文件)及后端服务的其它设置。我们假设启动文件的名称为app.ts,则ts的启动命令就为ts-node app.ts。启动文件的内容如下:

    import express from 'express'
    import bodyParser from 'body-parser'
    
    import UserRouter from './router/user'
    import RoleRouter from './router/role'
    
    const app = express()
    
    const BASE_ROUTER = '/router-base-url'
    
    app
        .use(bodyParser.json())
        .use(bodyParser.urlencoded({ extended: false }))
    
    app.use(`${BASE_ROUTER}`, UserRouter)
    app.use(`${BASE_ROUTER}`, RoleRouter)
    
    app.listen('8080', () => {
        console.log('server started at 8080')
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在启动文件中:

    1. 首先引入express,并初始化和监听服务启动端口,本示例中使用的是8080端口
    2. body-parser是express的一个中间件,用于对post请求的请求体进行解析
    3. 我们引入了两个接口文件./router/user./router/role,并注入到框架中,使用的是app.use()方法

    这样一来,只需要执行ts-node app.ts命令,我们就可以启动该服务了。下面我们开始编写接口文件。

    3. 数据库连接

    在编写接口文件之前,首先需要确定数据库的连接,使用knext框架。

    import config from "../config/db";
    import knex from "knex";
    
    const kenxConfig = {
        client: 'mysql2',
        connection: {
            host: config.host,
            port: config.port,
            user: config.user,
            password: config.password,
            database: config.database,
            timezone: '+08:00'
        },
        log: {
            error(message: any) {
                console.log('[knex] error', message)
            },
            warn(msg:any) {
                const ignore = '.returning() is not supported by mysql and will not have any effect.'
                if (msg.indexOf(ignore) === -1) {
                    console.info('[knex] warn', msg)
                }
            },
            debug(msg:any) {
                console.log('[knex] debug', msg)
            },
            deprecate(method: string, alternative: string) {
                console.log(method, alternative)
            }
        }
    }
    
    export default knex(kenxConfig)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    引入knex包,在连接时需要指定mysql客户端、配置连接信息(包括数据库域名、端口号、用户名、密码、时区等),还可以设置数据库连接的日志信息,本次示例中我们添加了errorwarndebugdeprecate。其中:

    • warn中,我们配置了.returning() is not supported by mysql and will not have any effect.日志不打印,这是kenx的日志。

    最后将knex(kenxConfig)导出,用于在数据库查询方法中调用。

    4. 数据库查询

    knex内置了很多基础方法,如数据库.select().insert().del()等,可以直接使用。也可以直接编写sql传入,对于复杂的数据库语言,就需要我们编写sql语言或将多个knex方法结合使用。knex中直接使用sql语言可以使用.raw()方法。

    下列是我们所封装的knex基础类,其中只包括了.select().insert().update(),其它方法可以参考Knex官网进行自行封装,我们将该文件命名为base.ts

    import knex from "./knex";
    
    class Base {
        table: string
        constructor(props: any) {
            this.table = props
        }
        // 查找
        all() {
            return knex(this.table).select()
        }
        // 更新
        update(update: Record<string,any>, params: Record<string,any>) {
            return knex(this.table).where(update).update(params)
        }
        insert(params: Record<string, any>) {
            return knex(this.table).insert(params)
        }
    }
    export default Base
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    这是一个基于ES6的类,调用该类可以传入一个参数,并将其赋值给table,所以应该传入数据库表名。

    1. knex(this.table).select()this.table中查询全部数据
    2. knex(this.table).where(update).update(params)用于对数据库update。update是更新条件的参数,params是跟新参数,均为对象格式。
    3. knex(this.table).insert(params)向表中插入数据,params是插入参数,也为对象格式。

    上述是封装的基础类,还可以封装一些复杂的方法。如下列代码封装了一个联表查询的方法queryList,这是一个.ts文件,可以看到,该查询中分别关联了三个表table1table2table3,并且进行的分页查询。

    将该文件命名为base-query.ts

    import Base from "./base";
    import knex from "./knex";
    
    class QueryModule extends Base {
        constructor(props = '') {
            super(props)
        }
    
        async queryList(page: number, pageSize: number, params: Record<string, any>) {
            return new Promise(async(resolve, reject) => {
                const start = (page - 1) * pageSize
                const { id } = params
                const sql_params: Object | Boolean  = !!params ? { 'table1.id': id } : 1==1
    
                let list = await knex('table1').where(sql_params).where('table1.flag', '=', 1).where('table1.statue', '=', 1)
                    .limit(pageSize).offset(start)
                    .leftJoin('table3', function() {
                        this.on(`table1.id`, '=', 'table3.id ')
                    })
                    .leftJoin('table2', function() {
                        this.on('table3.group_id', '=', 'table2.id')
                    })
                    .select('table1.*', 'table2.name as name2', 'table2.description as description2', 'table2.id as groupId')
                    .orderBy('table1.created_at', 'desc')
    
                resolve(list)
            })
        }
    }
    
    const query = new QueryModule()
    export default query
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    在上述方法中,我们还借助了promise,将查询到的结果通过resolve方法返回,便于在接口实现部分进行调用。

    5. 编写接口

    下面的代码我们封装了一个query-list的post请求接口。首先引入上一步封装好的类,并调用该方法即可。

    import express from "express";
    import queryModule from './base-query'
    
    const router = express.Router()
    
    router.post('/query-list', async(req:any, res:any, next: any) => {
    
        const { page, pageSize, param } = req?.body
        let data = await queryModule.queryList(page * 1, pageSize * 1, param)
    
        const list = JSON.parse(JSON.stringify(data))
    
        const r = new R()
        res.send(r.ok(list))
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    说明:

    • R类是一个封装的接口响应格式的类,可以自行封装。从代码中可以看出,该类的.ok方法是用于响应正确的结果,还有.err方法对失败结果进行响应。
    • 这个文件就是接口请求的controller类,你也可以将具体实现封装为单独的module进行调用,上述代码是我们简化过的一个方法。

    总结

    在这个过程中,你还需要:

    1. 使用ts的一个好处就是,可以明确的知道每个方法的参数格式、返回格式。如在封装kenx类时,每一个.select().insert().del()的参数都可以很明确的知道是对象格式Record。毕竟是使用经封装过的框架,对于不熟悉的人或时间太久再次阅读代码,这种格式可以很方便的提示开发者,该方法中的参数是什么格式。
    2. 该项目的启动需要正确配置typescript,才能够使用。否则无法使用importexport等命令。
    3. knex封装了很多个基础的方法,可以查看它的官方文档(地址:knex操作指南)。我们还使用了它的很多高级用法,在本示例中没有体现,如使用事务knex.transaction等。
    4. knex是一个方便的数据库操作库,可以多多使用它的一些方法
  • 相关阅读:
    计算机内用新的conda 环境迁移以及手工安装pybedtoolsconda 打包环境
    MySQL 数据库主从复制
    Linux——pxe中ks脚本增强
    【HMS Core】华为分析SDK如何申请数据导出功能?
    【Java基础】基本数据类型
    一篇文章带你入门HBase
    PyQT5 普通按钮互斥选中
    STM32实现光照强度传感器(BH1750)(标准库与HAL库实现)
    HCNP Routing&Switching之端口隔离
    乘风而起!企业级应用软件市场迅猛发展,有哪些机会可以把握?
  • 原文地址:https://blog.csdn.net/qq_29517595/article/details/134446153