1.创建Express项目
创建express
项目可以通过使用express-generator
脚手架,通过它,一个命令就会自动生成项目所需要的结构了。
1
2
3
4
5# 安装
npm i -g express-generator@4
# 创建项目(到指定文件夹下)
express --no-view 项目名称--no-view
参数的意思是不需要任何视图模板,适用于专门做后端接口的项目创建。
1 | # 安装依赖 |
现在就可以通过 http://localhost:3000
,来访问项目了。
现在对项目中的内容每次修改之后,都需要重启项目来查看,那么是否有更好的方式能够实现热修改呢?
2.nodemon监听修改
1 | # 安装 |
然后打开项目根目录下的package.json
,将start
这里修改为
1 | "scripts": { |
再次重启服务即可使用
3.项目结构
bin/www
:在package.json
中,大家见过这个文件的配置。它是用来启动项目的文件,无需修改,也不用管它,知道它是干嘛的就好了。public 目录
:这里放的各种静态资源,例如 CSS、图片等等静态资源。但因为我们项目是专门开发接口的,所以这里的东西,大家完全不需要管它,根本用不上。routes
:程序的路由部分,路由简单的理解就是将不同网址分别对应到不同的程序代码上去。app.js
:这个文件也很重要,在开发中,我们需要做一些路由的配置、跨域配置,都会来修改它的。
4.使用 Docker 运行 MySQL
docker compose
在项目根目录中,新建一个文件,叫做docker-compose.yml。千万要注意,一定要在项目根目录中,放在其他地方会找不到的。然后将下面的配置复制进去,这就是MySQL的一个简单配置了。
1 | services: |
运行docker-compose up -d
下载mysql,完成后到Docker中启动数据库服务,在本机可以通过Navicat
,http://localhost:3307
进行访问。
5.Sequelize ORM
Sequelize 是一个用于 Node.js 的流行的对象关系映射(ORM)库。
它主要有以下特点和作用:
- 简化数据库操作:通过将数据库表和操作映射到 JavaScript 对象和方法,使开发者可以使用面向对象的方式与数据库进行交互,而无需直接编写复杂的 SQL 语句。
- 提供模型定义:可以方便地定义数据库中的模型(表结构),包括字段、关系等。
- 数据操作接口:提供了诸如创建、读取、更新、删除(CRUD)等操作的方法,以及处理关联模型的能力。
- 数据库连接管理:帮助管理与数据库的连接和相关配置。
- 可移植性:使得应用在不同的数据库系统(如 MySQL、PostgreSQL、SQLite 等)之间切换相对容易,只需更改少量配置。
5.1 Sequelize ORM 的使用
1 | # 安装 |
在当前项目目录下安装所依赖的sequelize包和对数据库支持依赖的mysql2
1 | npm i sequelize mysql2 |
初始化项目
1 | sequelize init |
可以看到,提示我们,创建了一个config配置文件和三个目录,这些就是sequelize所需要的东西了。
1 | . |
- config.json:sequelize连接数据库的配置文件,配置数据库连接需要的相关信息
- migrations:是迁移的意思,如果你需要对数据库做新增表、修改字段、删除表等等操作,就需要在这里添加迁移文件了。而不是像以前那样,使用客户端软件来直接操作数据库。
- models:这里面存放的是模型文件,当我们使用sequelize来执行增删改查时,就需要用这里的模型文件了。每个模型都对应数据库中的一张表。
- seeders,是存放的种子文件。一般会将一些需要添加到数据表的测试数据存在这里。只需要运行一个命令,数据表中就会自动填充进一些用来测试内容的了。
config.json
文件的内容如下:
1 | { |
5.2创建模型
1 | sequelize model:generate --name Article --attributes title:string,content:text |
打开models/article.js
。可以看到,在模型文件夹中,出现了一个叫做Article
的模型,它里面有标题和内容。
标题是字符串类型,对应到 MySQL
数据库里,它就会自动变成varchar
。内容部分,则是text
类型。
5.3迁移文件
在migrations
文件夹,里面出现了一个由当前时间,加上create-article
命名的文件,这个文件就是迁移文件了。它的作用就是用来创建、修改表的。
看到这里,在up部分。我们通过createTabel,创建了一个叫做Articles的表。
大家注意哦,这就是我之前和大家说的,模型名字是单数:Article。但是表的名字一定是复数形式,也就是Articles。这是sequelize里的命名规则,一定不要搞错了。如果你的单复数搞错了,那sequelize就找不到对应的东西了。
接着往下看,这些就是定义了Articles这张表里面所拥有的字段了,比方说id、title、content,这些外还出现了两个时间字段createdAt和updatedAt。
这两个字段,当在新增或修改数据的时候,sequelize会自动的帮我们填写的。
这里的内容,已经非常好了,我们只需要做一个小小的调整。就是title部分,我需要它不为null,所以加上allowNull: false,。content部分,我们就不管它了,已经很好了。
至于down部分,是新建表的反向操作,里面写的是dropTable,也就是删除当前的表。这样当我们创建表,建完后,突然又发现有错误,也可以通过相关命令来删除当前表。
运行迁移文件:sequelize db:migrate
打开数据库客户端,刷新一下,可以看到Articles表又神奇的出现了。看一看结构选项卡,里面的字段和我们当时自己手动创建的完全一样,而且还多了两个时间字段。这就是迁移文件的作用了。
5.4 种子文件
现在表也有了,下一步就是要填充一些在开发中用来测试的数据了。当然你可以用手动往里面一点点填,但很多情况我们做测试,可能需要非常多的数据。例如我希望数据库里有 100 篇文章,这时候,我们一条条的录入也太笨了点。最简单的方法就是使用种子文件了。再来试试这条命令
1 | sequelize seed:generate --name article |
完成后,在seeds
目录,就看到刚才命令新建的种子文件了。同样也是分为两个部分,up
部分用来填充数据,down
部分是反向操作,用来删除数据的。
通过up
方法往Articles
表里插入数据。
1 | async up (queryInterface, Sequelize) { |
down
部分进行修改
1 | async down (queryInterface, Sequelize) { |
执行种子文件,往数据库中插入数据
1 | sequelize db:seed --seed seeds目录下的js文件名字(如xxx-article) |
6.CRDU接口
6.1 处理路由文件
项目的路由文件存放在routes
文件夹下,对于每一类待写的接口,都要在routes
文件夹下创建新的js文件。
1 | var express = require("express"); |
6.2 app.js
在app.js
文件中引入创建的路由文件。
例如:
1 | var articlesRouter = require("./routes/admin/articles"); |
其中,/admin/articles
就是新创建的路由的地址。例如:http://localhost:3000/admin/articles
可以访问其中的接口。
6.3处理接口返回信息
对于接口返回信息而言,分为请求成功和请求失败两种情况。对于这两种情况,一般需要返回状态码、数据、信息等,因此可以将其抽取出来封装成为公共方法。
一般将这类方法存在根目录下的utils
文件夹中。
1 | // response.js 文件 |
6.4 接口
对于接口方法中的通用功能,可以将其抽取成为一个公共方法。
例如,根据id号查询具体文章方法在多个借口中都会使用到。
1 | /** |
1 | // 增:创建文章接口 |
7.企业内项目开发的流程
- 编写需求文档
- 原型与UI设计
- 确定数据库的表、字段以及接口地址和数据
- 同时进行:前端mock开发、后端接口开发
- 接口开发完成后,将mock地址更换为接口地址
- 测试、上线部署
8.建立数据库表
回滚迁移:sequelize db:migrate:undo
,运行命令后,会回滚上一次运行的迁移,也就是删掉Article
表。
以创建Courses
表为例:
- 运行创建命令:
sequelize model:generate --name Course --attributes categoryId:integer,userId:integer,name:string,image:string,recommended:boolean,introductory:boolean,content:text,likesCount:integer,chaptersCount:integer
- 修改
migrations
文件夹下的对应course
文件: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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68;
const { toDefaultValue } = require("sequelize/lib/utils");
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable("Courses", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER.UNSIGNED,
},
categoryId: {
allowNull: false,
type: Sequelize.INTEGER.UNSIGNED,
},
userId: {
allowNull: false,
type: Sequelize.INTEGER.UNSIGNED,
},
name: {
allowNull: false,
type: Sequelize.STRING,
},
image: {
type: Sequelize.STRING,
},
recommended: {
type: Sequelize.BOOLEAN,
},
introductory: {
type: Sequelize.BOOLEAN,
},
content: {
type: Sequelize.TEXT,
},
likesCount: {
allowNull: false,
defaultValue: 0,
type: Sequelize.INTEGER.UNSIGNED,
},
chaptersCount: {
allowNull: false,
defaultValue: 0,
type: Sequelize.INTEGER.UNSIGNED,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
});
await queryInterface.addIndex("Courses", {
fields: ["categoryId"],
});
await queryInterface.addIndex("Courses", {
fields: ["userId"],
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable("Courses");
},
};