如何使用node+mysql+express实现一个基础的后台服务

前言


这是一篇在痛苦与折磨中诞生的文章,俗话说 "打铁还需自身硬",身为一个前端切图仔,本身水平浅薄,但是因为兴趣使然,想着自己也搞一个后台服务自己玩玩(主要是对其它后端语言并不是很熟悉)。也就选择了一个相对熟悉的下手(node)。

为什么说是在痛苦与折磨中诞生的,因为我不会,在逛遍各大社区并没有找到适合自己的技术文章之后,只能踩坑前行,也希望这篇文章能给其他从事前端工作并也想了解后端的同志们一个便于理解和实践的入口,共勉!


  • 什么是node,node为什么能实现后台服务?

  • 什么是MySQL,MySQL如何与node服务进行连接交互?

  • 什么是express,他在node中的起到的作用是什么?

  • node服务端代码实践。

* 什么是node,node为什么能实现后台服务?


node是一个基于chrome V8引擎的Javascript运行时环境。通常来说,我们的js代码只能运行在浏览器中,但是因为有了node,它为我们提供了JavaScript运行时环境,我们的js代码就可以脱离浏览器在不同的地方运行。

这也理所当然我们JavaScript代码可以运行在服务器端。加上node所拥有的特性,单线程与事件循环、非阻塞i/o,NPM丰富的生态系统,使得node搭建的服务端某种程度上来说算的上高性能后台服务(当然,node也有它本身的劣势,这里不一一细说,有兴趣可以自行查阅资料)


什么是MySQL,MySQL如何与node服务进行连接交互?


MySQL是一种关系型数据库管理系统,是一种基于表格结构的数据库,数据以行和列的形式组织,用于存储和管理我们应用程序的数据。而我们要实现node服务端程序连接数据库并交互需要使用到第三方模块,也就是使用npm下载我们所需的依赖(后面以代码的形式详细讲解)。

然后创建一个我们自己的MySQL连接池,使用连接池获取连接对象,并执行sql语句实现我们Node程序与MySQL的连接与交互。


什么是express,它在node中起到的作用是什么?


简单来说express是基于node的web应用程序框架,它简化了我们自身使用node搭建应用程序的过程。难道仅仅是因为这个我们才使用的它吗,当然不是,它不仅经简化了我们搭建应用程序的过程,它还为我们提供了灵活且强大的功能和工具。

例如,处理http请求、路由、模板引擎,错误处理等简洁的API和中间件,使我们可以更专注于业务逻辑的实现,而不用苦恼于底层的问题。(express只是一种选择,其它的框架还有Koa、hapi等)。就像是其它的一些前端框架一样,我们可以在这些框架上快速上手,快速开发。


node服务端代码实践

第一步(搭建项目)

先检查自己的电脑中有没有安装node,打开电脑的命令窗口,输入node -v回车之后有无版本号出现,如果有那么就代表你已经有或安装好node了,如果没有,自行去node官网下载安装。

那么接下来让我们创建一个文件夹为node-project,创建完之后,进入这个文件夹,右击打开终端,并输入npm init初始化项目并生成package.json文件,其中包含了项目的相关信息和依赖。

初始化完成之后,我们需要先下载我们目前所需的依赖,这里就用到了express这个框架,终端执行 npm express,express用于创建我们的node服务器。

这里我们先不进行代码的编写,而是去了解我们搭建一个node服务端整体的目录结构,都由哪些组成,他们都分别负责那一部分。我个人觉得,窥全貌,览细节,更适合我们去搭建一个完整项目应该拥有的思维。只有我们知道了一个东西的组成部分,并且了解其组成部分的职责与功能,才能更好的去实现它。

就像搭积木一样,有图纸,有构成部分讲解和没图纸,没构成部分讲解,搭建起来论速度算得上天壤之别(当然,排除你是天才,如果你是,可以忽略这段话。)

下图是我自己的node服务端项目目录结构

图片中的文件夹与目录算是一个node服务端相对完整的目录结构,当然还有很多扩展的东西没添加在其中,不过对于刚刚入手node服务端的同志们应该是够用了,下面让我们一一解释这些文件夹都有什么作用,都是什么。

node-project文件下面的一级文件

  • src目录是用于存放我们项目源代码的地方,用于区分其他类型的文件和资源
  • index.js文件是我们node程序的入口文件,用于启动项目的文件(图片中是ts文件类型,问题不大,我这里是我自己的项目用的ts,你们学习的过程中直接创建js文件就行,我会把ts里的一些类型等等一些东西大致去除,不妨碍使用js写的跟ts代码不适配)。
  • nodemon.json文件的配置就是我们前端中常听到的一个词"热更新",用于我们启动项目之后,修改了其中的代码,当监听到我们代码更改时进行重新启动并编译代码。如果不配置的话,那我们每次修改代码之后都要断开项目并重新启动。
  • package.json文件是我们初始化项目时产生的文件,里面的信息主要包含了定义项目的名称、版本号、描述,指定项目的许可类型和作者信息等基本信息。并列出了项目依赖的各种模块和软件包,并指定了其版本号。也可以在其中自定义脚本命令,如启动服务器,打包应用程序等。
  • src目录下文件含义
  1. controller文件夹主要用于存放处理请求和响应的代码,这也是对代码的一种解耦,这里只处理请求和响应的值,不做业务的处理和数据的操作,当然它也可以直接处理请求并返回响应数据但是我们不建议这样去做。

2. db文件夹主要存放的是与数据库建立连接所需的配置信息,如数据库的主机名,用户名,密码等, 这个配置文件可以被应用程序的其他部分引用,用于建立数据库的连接。

  1. mapping文件夹主要用于存放对不同数据表进行操作的sql语句,因为我们是学习并了解后端,所以我这里的sql语句全部是自己原生手写的,加深理解和印象。当然如果你想快速的进行服务端开发,并不想深入了解这些sql语句,也可以使用ORM框架来代替这一部分。(我常用的ORM框架是sequelize,感兴趣的话,也可以专门出一期ORM实现的node服务端应用程序)

4. router文件夹主要存放了路由的配置,和不同URL请求的处理程序或控制器,将http请求方法与url路径映射到控制器操作。

5. service文件夹负责处理业务逻辑和数据操作,它位于控制器(Controller)和数据库之间,主要负责处理业务逻辑、调用数据访问层(DAO)进行数据操作,并将处理结果返回给控制器或其他上层模块。

6. utils文件夹,翻译过来就是工具的意思(网络释义),这里的代码并没有所谓的限制,简单来说,对你有用的,你觉得有用的代码,都可以放到里面当成自己的工具去使用。

7. index(这里怎么还有一个index文件),先前的index是入口,并不包含启动代码,而这个文件里面的代码是真正启动这个应用程序的代码。

  • 综上所述,我为什么针对与目录与文件夹说了这么久,主要还是为了让大家更清晰的知道自己在干什么,干的有什么用,而代码为什么要分这么多文件夹,简述:使代码更具可读性和可维护性并降低代码的耦合性。(这是个万能公式)。

正文开始

js 复制代码
package.json --文件配置
{
  "name": "### node-project", 项目名称
  "version": "1.0.0",项目版本呢
  "description": "### node-project",描述
  "main": "index.ts",模块入口文件
  "scripts": {可执行的脚本命令
    "start": "nodemon",用于启动应用程序的命令(其中nodemon下面会讲解)
  },
}
js 复制代码
下载依赖,npm install nodemon
创建nodemon.json文件
nodemon.json(这个文件就是启动项目之后,监听修改代码之后并重新启动项目,所谓热更新)
{
  "watch": ["src/**/*.js", "./index.js", "utils/**/*.js"],监听src文件夹下所有文件的改动,监听index.ts文件的改动,监听utils文件下所有文件的改动。
  "ignore": ["node_modules"],忽略依赖包中代码文件改动
  "exec": "node index.js",执行启动代码文件
  "ext": ".js"文件扩展名
}
js 复制代码
index.js
//主入口文件

//引入src入口文件
import run from './src'; //这里引入的是src下面的index.js文件

//运行Node服务,并传入端口
// run(config.server.port);
run(8686);
js 复制代码
src/index.js
//真正的应用启动程序代码
import router from './router'; //引入所需的router文件夹中的代码,里面包含了我门配置和定义的路由
import { repool } from './db'; //这里引入的是我们的数据库配置的文件,我门称之为数据库连接词
import type { Server } from 'http';//从http模块导入server类型,http是node.js自带的一个核心模块,提供了http服务器和客户端功能。
import express from 'express';//从express模块中导入express对象,代码中我们使用它来创建、配置并运行express应用程序
import bodyParser from 'body-parser';//下载body-parser依赖,npm install body-parser,body-parser是一个中间件,用于解析http请求的请求体(就是处理请求中的请求数据)
import cors from 'cors';//下载cors依赖,npm install cors,cors是一个用于express应用程序的node.js中间价,用于处理跨域资源共享(跨域方面知识自行了解)
const apply = express();//创建一个express应用程序实列
apply //这里使用use把我们上面引入和依赖对象挂载到express应用程序的实例上,包含路由,跨域,请求解析等
  .use(bodyParser.urlencoded({ extended: false }))
  .use(bodyParser.json())
  .use(cors())
  .use(router);

repool();//这一步是启动数据库连接词连接我们的数据库(后面会讲到)
const run = (port: number | string | undefined): Server => {
//这里定义了一个run函数,主要代码功能是,启动express应用程序并监听指定的端口号
  return apply.listen(port, () => {
    console.log(port, 'successfully---boilng');
  });
};
export default run;//这里我们导入run函数,以便在入口文件中使用。
js 复制代码
db/index.js文件
//数据库连接池配置
//下载mysql12依赖,并导入mysql对象
import mysql from 'mysql2';
const pool = mysql.createPool({ //创建一个数据库连接实例
  connectionLimit: 20, //最大连接数
  host: 'localhost'//这里是ip主机地址,因为此次示例中我们都是本地项目启动,包括数据库也是本地的,所以直接在这里使用localhost
  user: 'boilng_node', //数据库用户名称
  password: 'boilng3306', //数据库用户密码
  port: 3306, //数据库端口
  database: 'boilng_node', //数据库表名称
  connectTimeout: 5000, //连接超时
});
const repool = () => {//创建一个函数,函数中的代码功能时启动连接数据库并监听是否连接成功
  pool.on('connection', (stream) => {
    console.log('someone connected!');
  });
};
export { pool,repool };导出函数实例repool
js 复制代码
router/index.js
//此文件存放我们的路由配置代码,这里只展示一个示例
import AdminController from '../controller/AdminController'; //这里是引入我们请求/响应层文件
import express from 'express'; //导入express
const router = express.Router(); //创建路由对象

//分页查询用户数据
router.post('/admin/queryUserList', UserController.getUserList);//这里是配置我们的路由路径,和指向响应的接口处理方法
export default router;//到处路由以便使用
js 复制代码
controller/AdminController.js 
//可以理解为这个是用于处理管理员用户数据的类,里面包含了对数据进行增删改查的接口方法,这里只展示了查询管理员用户的信息
import AdminService from '../service/AdminService';//这个是引入我们业务层代码文件,用于把相应接口方法的响应数据处理并返给我们
class AdminController {//定义一个AdminController类
  /**
   * 获得管理员列表分页数据
   * @param {Context} ctx
   */
  getAdminList(req: any, res: any) { 
    const { limit, page } = req.body;//这里是拿到请求体中的数据,page页数,limit条数
    AdminService.getAdminListPage({ limit, page }) //这里是调用处理业务层的代码方法,并把我们的请求参数传入进行后续处理
      .then((result) => {
        res.json(result);//这里是响应成功之后返回处理好的响应数据
      })
      .catch((err) => {
        res.json({code:400,msg:'error'});//这里是请求失败返回的数据
      });
  }
}
export default new AdminController();实例化这个类并导出
js 复制代码
service/AdminService.js
//处理接口业务逻辑的代码类
import { pagingQuery } from '../mapping/sqlmap'; //引入存放sql语句的代码文件,里面包含了我们用于与数据库各种操作的sql语句
import connectionQuery from '../utils/connectionQuery';//这里是我封装的一个简易工具类,用于执行sql语句并拿到数据库返回值
class AdminService {
  /**
   * 分页查询管理员数据
   * @param page 页数
   * @param limit 条数
   * @returns
   */
  getAdminListPage(data: any) {
    return connectionQuery(
      pagingQuery(Number(data.page), Number(data.limit), 'admin')//利用我们封装的执行sql语句的方法拿到传入的参数,进行与数据库的操作
    );
  }
 }
 export default new AdminService();//实例化并导出这个类
js 复制代码
mapping/sqlmap.js
 /**
   * 分页查询
   * @param {number} page 查询页数
   * @param {number} limit 查询条数
   * @param {string} table 查询表
   * @returns {string} 返回值为string类型
   */
   const sqlmap = {
      pagingQuery: (page: number, limit: number, table: string) => {
        const offset = (page - 1) * limit;
        return `select * from ${table} limit ${limit} offset ${offset}`;//这个就是sql语句,这里就不详细去说了,自行了解
      }
  }
 const { pagingQuery } = sqlmap;//解构sqlmap对象es6语法
export { pagingQuery };//导出分页查询方法
js 复制代码
utils/connectionQuery.js 
//对执行sql语句的方法进行封装
import { pool } from '../db/index';//引入数据库配置文件中的数据库实例
var connectionQuery = (sql: string) => { 
  return new Promise((resolve, reject) => {
    pool.getConnection((err, conn) => {
      if (err) {
        resolve(err);//错误的回调,返回错误信息
      }
      conn.query(sql, (err: any, result: any) => { //执行查数据库查询
        conn.release();//释放数据连接,以免占用过多的资源导致系统崩溃或者运行缓
        if (err) {
          reject(err);以免占用过多的资源导致系统崩溃或者运行缓
        }
        resolve(result);//成功的回调,返回从数据库拿到的数据
      });
    });
  });
};
export default connectionQuery;//导出执行sql的方法

总结

根据以上代码操作,成功跑通一个与node应用程序与数据库操作的服务端应该是不成问题的,第一次写文,可能写的地方会让各位有不明了的地方(见谅),文章写的水平可能也不怎么样,但是初衷就是只要能给到大家帮助,哪怕一点点,也算这个文章是有些许的价值了。主要也算是帮助以前的自己了。

再次总结一下,整体进食步骤:了解 --> 项目结构与含义 --> 功能代码,文件的引用与含义。 我想,耐心的去阅读一遍,总归是有收获的。 有什么问题,或者文章中错误的地方,可在评论留言,我会一一修改与回复(谢谢大家!)

相关推荐
该用户已不存在10 小时前
程序员的噩梦,祖传代码该怎么下手?
前端·后端
间彧11 小时前
Redis缓存穿透、缓存雪崩、缓存击穿详解与代码实现
后端
摸鱼的春哥11 小时前
【编程】是什么编程思想,让老板对小伙怒飙英文?Are you OK?
前端·javascript·后端
Max81211 小时前
Agno Agent 服务端文件上传处理机制
后端
调试人生的显微镜11 小时前
苹果 App 怎么上架?从开发到发布的完整流程与使用 开心上架 跨平台上传
后端
顾漂亮12 小时前
Spring AOP 实战案例+避坑指南
java·后端·spring
间彧12 小时前
Redis Stream相比阻塞列表和发布订阅有哪些优势?适合什么场景?
后端
间彧12 小时前
Redis阻塞弹出和发布订阅模式有什么区别?各自适合什么场景?
后端
苏三说技术12 小时前
统计接口耗时的6种常见方法
后端
SimonKing12 小时前
Mybatis-Plus的竞争对手来了,试试 MyBatis-Flex
java·后端·程序员