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

news/2024/7/7 22:36:34

前言

首先,我们对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

安装这些依赖之前,首先要确定正确下载安装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. 首先引入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)

引入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

这是一个基于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

在上述方法中,我们还借助了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))
})

说明:

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

总结

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

  1. 使用ts的一个好处就是,可以明确的知道每个方法的参数格式、返回格式。如在封装kenx类时,每一个.select().insert().del()的参数都可以很明确的知道是对象格式Record<string,any>。毕竟是使用经封装过的框架,对于不熟悉的人或时间太久再次阅读代码,这种格式可以很方便的提示开发者,该方法中的参数是什么格式。
  2. 该项目的启动需要正确配置typescript,才能够使用。否则无法使用importexport等命令。
  3. knex封装了很多个基础的方法,可以查看它的官方文档(地址:knex操作指南)。我们还使用了它的很多高级用法,在本示例中没有体现,如使用事务knex.transaction等。
  4. knex是一个方便的数据库操作库,可以多多使用它的一些方法

http://lihuaxi.xjx100.cn/news/1799549.html

相关文章

odoo16前端框架源码阅读——env.js

env.js&#xff08;env的初始化以及服务的加载&#xff09; 路径&#xff1a;addons\web\static\src\env.js 这个文件的作用就是初始化env&#xff0c;主要是加载所有的服务。如orm, title, dialog等。 1、env.js 的加载时机 前文我们讲过前端的启动函数&#xff0c;start.…

【Effective C++ 笔记】(四)设计与声明

【四】设计与声明 条款18 &#xff1a; 让接口容易被正确使用&#xff0c;不易被误用 Item 18: 让接口容易被正确使用&#xff0c;不易被误用 Make interfaces easy to use correctly and hard to use incorrectly. “让接口容易被正确使用&#xff0c;不易被误用”&#xff0…

CopyOnWriteArrayList 源码详解

目录 一. 前言 二. 源码详解 2.1. 类结构 2.2. 属性 2.3. 构造方法 2.4. add(E e) 2.5. add(int index, E element) 2.6. addIfAbsent() 方法 2.7. 获取元素() 方法 2.8. remove(int index) 2.9. size() 三. FAQ 3.1. 为什么CopyOnWriteArrayList没有size属性&…

linux rm 删除找回的几种方法

对于rm,很多人都有惨痛的教训。我也遇到一次&#xff0c;一下午写的程序就被rm掉了&#xff0c;幸好只是一个文件&#xff0c;第二天很快又重新写了一遍。但是很多人可能就不像我这么幸运了。本文收集了一些在Linux下恢复rm删除的文件的方法&#xff0c;给大家作为参考。 1.几…

python趣味编程-5分钟实现一个Flappy Bird游戏(含源码、步骤讲解)

Python 中的 Flappy Bird 游戏可以免费下载开源代码,它是为想要学习 Python 的初学者创建的。 该项目系统使用了 Pygame 和 Random 模块。 Pygame 是一组跨平台的 Python 模块,专为编写视频游戏而设计。 Python 中的 Flappy Bird 代码 – 项目信息 项目名称:Python 中的 Fl…

bat随手记

目录 bat批处理常用命令查询有哪些reg命令&#xff0c;帮助信息——reg /?查询注册表信息——reg query /?切换到批处理文件目录——cd /d "%~dp0"永久设置环境变量——setx命令设置注册表内容——/v名称&#xff0c;/t类型&#xff0c;/d数据%cd%和%~dp0的区别/f没…

第十九章,Java绘图

Graphics类 Graphics类是所有图形上下文的抽象基本类&#xff0c;它允许应用程序在组件以及闭屏图像上进行绘制 Graphics类封装了Java支持的基本绘图操作所需的状态信息&#xff0c;主要包括颜色、字体、画笔、文本、图像等 Graphics类提供了常用的绘图方法&#xff0c;利用这些…

Ubuntu20.04安装Beyond Compare 4.4.7

参考链接&#xff1a; 1.Ubuntu20.04 Beyond Compare 4.3.7 安装 2.Ubuntu20.04安装Beyond Compare 4.3.7