前言
对于前端开发者而言,若希望快速搭建一个轻量级后端服务,Express 配合 MongoDB 是一个高效且易上手的选择。该技术栈特别适用于对开发效率要求高、业务数据结构相对独立、关联性不强的场景。若你的业务涉及复杂的关联关系或高事务一致性要求,建议考虑使用关系型数据库(如 MySQL)或更全面的后端框架,以避免后期维护上的困难。
我将以一个"账单管理系统"为例,带你从零开始搭建一个具备用户认证、数据增删改查等常见功能的 API 服务。无论你是想学习后端开发,还是需要为自己前端项目提供接口支持,本文都将为你提供清晰的实现路径。
一、项目创建
安装express-generator工具创建Express项目骨架,
less
npm i express-generator -g
//使用该工具快速搭建项目结构和基础代码。
express -e projectName //-e:添加ejs页面模板支持
npm install //安装依赖
我们将在生成的基础上,调整为一个更清晰的服务端 API 项目结构:
bash
accounts/
├── app.js # 应用入口
├── bin/www # 服务启动脚本
├── config/config.js # 配置文件(数据库、JWT 密钥等)
├── db/db.js # 数据库连接
├── models/ # 数据模型
│ ├── AccountModel.js
│ └── UserModel.js
├── middlewares/ # 中间件
│ └── checkTokenMiddleware.js
├── routes/ # 路由
│ ├── api/ # API 接口
└── public/ # 静态资源
二、路由设计
2.1 路由模块化
这里定义的路由就是前端请求的接口,你肯定再熟悉不过了。Express 使用 express.Router() 实现路由模块化,便于管理和维护:
javascript
// routes/api/accounts.js
const express = require('express');
const router = express.Router();
router.get('/account/:id', (req, res) => {
let { id } = req.params; // 获取路由参数
/* ... */
});
router.post('/account', function(req, res) { /* ... */ });
module.exports = router;
在入口文件中挂载路由:
javascript
// app.js
var accountRouter = require('./routes/api/accounts');
app.use('/api', accountRouter); // 前缀 /api
2.2 RESTful API 设计
本项目的 API 遵循 RESTful 规范:
| 方法 | 路径 | 功能 |
|---|---|---|
| GET | /api/account | 获取账单列表 |
| POST | /api/account | 创建账单 |
| GET | /api/account/:id | 获取单个账单 |
| PATCH | /api/account/:id | 更新账单 |
| DELETE | /api/account/:id | 删除账单 |
三、项目中的中间件
怎么理解 Express中的中间件,你还记得vue中的插件吗?
我觉得可以将 Express 中间件看作是 Vue 插件的"服务器端版本":
- 都遵循 "单一职责原则" - 每个中间件/插件只做一件事
- 都通过 "注入"方式 增强核心框架
- 都有 "生命周期" 概念(中间件的 next() vs 插件的 install)
- 都支持 "链式组合" (中间件链 vs 插件组合)
不过记住,Express 中间件更侧重于请求处理 ,而 Vue 插件更侧重于应用扩展。
3.1 全局中间件
我们可以在应用入口app.js文件中看到大量中间件的注册:
php
//app.js
var express = require('express');
var path = require('path');
var logger = require('morgan');
//导入路由
var accountRouter = require('./routes/api/accounts');
var authApiRouter = require('./routes/api/auth');
//导入服务配置
const {DBHOST, DBPORT, DBNAME} = require('./config/config');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
// 解析 JSON 请求体
app.use(express.json());
// 解析 URL 编码的请求体
app.use(express.urlencoded({ extended: false }));
// 托管静态资源
app.use(express.static(path.join(__dirname, 'public')));
app.use('/api', accountRouter);
app.use('/api', authApiRouter);
//错误处理
app.use(function(req, res, next) {
res.render('404');
});
//错误处理
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
3.2 应用中间件到路由
我们也可以为特定路由添加中间件,实现全局路由鉴权(是不是就跟vue中的路由守卫差不多了!):
javascript
//routes/api/account.js
let checkTokenMiddleware = require('../../middlewares/checkTokenMiddleware');
// 所有 /account 相关路由都需要 token 验证
router.get('/account', checkTokenMiddleware, function(req, res) {
// ...
});
router.post('/account', checkTokenMiddleware, function(req, res) {
// ...
});
这里的中间件函数checkTokenMiddleware具体的作用我们在后面用户鉴权详细讲解。
四、数据库MongoDB安装
访问mongodb官方地址下载MongoDB Community Server
www.mongodb.com/try/downloa...
选择相应配置进行下载

配置步骤如下:
- 将压缩包移动到 C:\Program Files 下,然后解压
- 创建 C:\data\db 目录,mongodb 会将数据默认保存在这个文件夹
- 以 mongodb 中 bin 目录作为工作目录,启动命令行
- 运行命令 mongod
命令行窗口看到以下效果则代表MongoDB的服务启动成功了,我们便可以在项目中将api和数据库相结合。

五、数据库集成
npm i mongoose
5.1 MongoDB 连接封装
javascript
// db/db.js
//参数success接收一个回调函数
module.exports = function (success, error) {
const mongoose = require('mongoose');
const { DBHOST, DBPORT, DBNAME } = require('../config/config.js');
mongoose.set('strictQuery', true);
mongoose.connect(`mongodb://${DBHOST}:${DBPORT}/${DBNAME}`);
mongoose.connection.once('open', () => {
success();
});
mongoose.connection.on('error', () => {
error();
});
};
将服务器启动脚本作为回调函数传入到db封装函数中
ini
```javascript
// bin/wwww
const db = require('../db/db')
db(() => {
var app = require('../app');
var http = require('http');
//启动服务,监听端口
var port = normalizePort(process.env.PORT || '80');
app.set('port', port);
var server = http.createServer(app);
server.listen(port);
//...
}
)
通过上面的操作我们借助mongoose完成了对数据库的连接
5.2 Mongoose 模型定义
/account这个api模块涉及的字段、类型校验定义成一个模型
javascript
// models/AccountModel.js
const mongoose = require('mongoose');
let AccountSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
time: Date,
type: {
type: Number,
default: -1 // -1 支出,1 收入
},
account: {
type: Number,
required: true
},
remarks: String
});
let AccountModel = mongoose.model('accounts', AccountSchema);
module.exports = AccountModel;
结合数据模型,我们最后来完善api接口跟数据库的交互
javascript
//routes/api/account.js
//导入 express
const express = require('express');
//导入中间件
let checkTokenMiddleware = require('../../middlewares/checkTokenMiddleware');
const router = express.Router();
//导入 moment
const moment = require('moment');
const AccountModel = require('../../models/AccountModel');
//记账本的列表
router.get('/account', checkTokenMiddleware, function (req, res, next) {
//读取集合信息
AccountModel.find().sort({ time: -1 }).exec((err, data) => {
if (err) {
res.json({
code: '1001',
msg: '读取失败~~',
data: null
})
return;
}
//响应成功的提示
res.json({
//响应编号
code: '0000',
//响应的信息
msg: '读取成功',
//响应的数据
data: data
});
})
});
//新增记录
router.post('/account', checkTokenMiddleware, (req, res) => {
//表单验证
//插入数据库
AccountModel.create({
...req.body,
//修改 time 属性的值
time: moment(req.body.time).toDate()
}, (err, data) => {
if (err) {
res.json({
code: '1002',
msg: '创建失败~~',
data: null
})
return
}
//成功提醒
res.json({
code: '0000',
msg: '创建成功',
data: data
})
})
});
//删除记录
router.delete('/account/:id', checkTokenMiddleware, (req, res) => {
//获取 params 的 id 参数
let id = req.params.id;
//删除
AccountModel.deleteOne({ _id: id }, (err, data) => {
if (err) {
res.json({
code: '1003',
msg: '删除账单失败',
data: null
})
return;
}
//提醒
res.json({
code: '0000',
msg: '删除成功',
data: {}
})
})
});
//获取单个账单信息
router.get('/account/:id', checkTokenMiddleware, (req, res) => {
//获取 id 参数
let { id } = req.params;
//查询数据库
AccountModel.findById(id, (err, data) => {
if (err) {
return res.json({
code: '1004',
msg: '读取失败~~',
data: null
})
}
//成功响应
res.json({
code: '0000',
msg: '读取成功',
data: data
})
})
});
//更新单个账单信息
router.patch('/account/:id', checkTokenMiddleware, (req, res) => {
//获取 id 参数值
let { id } = req.params;
//更新数据库
AccountModel.updateOne({ _id: id }, req.body, (err, data) => {
if (err) {
return res.json({
code: '1005',
msg: '更新失败~~',
data: null
})
}
//再次查询数据库 获取单条数据
AccountModel.findById(id, (err, data) => {
if (err) {
return res.json({
code: '1004',
msg: '读取失败~~',
data: null
})
}
//成功响应
res.json({
code: '0000',
msg: '更新成功',
data: data
})
})
});
});
module.exports = router;
到了这一步我们就真正完成了一个模块的接口开发,你就不再只是一个单纯的前端开发者了,即使后面道阻且长!
六、用户认证
6.1 登录功能的实现(基于 JWT)
npm i jsonwebtoken
定义user用户模型
javascript
//导入 mongoose
const mongoose = require('mongoose');
//创建文档的结构对象
//设置集合中文档的属性以及属性值的类型
let UserSchema = new mongoose.Schema({
//标题
username: String,
password: String
});
//创建模型对象 对文档操作的封装对象
let UserModel = mongoose.model('users', UserSchema);
//暴露模型对象
module.exports = UserModel;
实现登录接口,用户登录成功后,服务器签发一个 JWT Token 返回给客户端:
javascript
//routes/api/auth.js
var express = require('express');
var router = express.Router();
//导入 jwt
const jwt = require('jsonwebtoken');
//导入配置文件
const { secret } = require('../../config/config')
//导入 用户的模型
const UserModel = require('../../models/UserModel');
const md5 = require('md5');
//登录操作
router.post('/login', (req, res) => {
//获取用户名和密码
let { username, password } = req.body;
//查询数据库
UserModel.findOne({ username: username, password: md5(password) }, (err, data) => {
//判断
if (err) {
res.json({
code: '2001',
msg: '数据库读取失败~~~',
data: null
})
return
}
//判断 data
if (!data) {
return res.json({
code: '2002',
msg: '用户名或密码错误~~~',
data: null
})
}
//创建当前用户的 token
let token = jwt.sign({
username: data.username,
_id: data._id
}, secret, {
expiresIn: 60 * 60 * 24 * 7
});
//响应 token
res.json({
code: '0000',
msg: '登录成功',
data: token
})
})
});
//退出登录
router.post('/logout', (req, res) => {
//销毁 session
req.session.destroy(() => {
res.render('success', { msg: '退出成功', url: '/login' });
})
});
//注册用户
router.post('/reg', (req, res) => {
//做表单验证
//获取请求体的数据
UserModel.create({ ...req.body, password: md5(req.body.password) }, (err, data) => {
if (err) {
res.status(500).send('注册失败, 请稍后再试~~');
return
}
res.json({
code: '0000',
msg: '注册成功',
})
})
});
module.exports = router;
客户端将token存放在接口的请求头中传递给服务端,服务端定义token校验中间件,路由调用该中间件从而实现后续业务接口的登录认证。
javascript
// middlewares/checkTokenMiddleware.js
const jwt = require('jsonwebtoken');
const { secret } = require('../config/config');
module.exports = (req, res, next) => {
// 从请求头获取 token
let token = req.get('token');
if (!token) {
return res.json({
code: '2003',
msg: 'token 缺失',
data: null
});
}
// 校验 token
jwt.verify(token, secret, (err, data) => {
if (err) {
return res.json({
code: '2004',
msg: 'token 校验失败',
data: null
});
}
// 保存用户信息到请求对象
req.user = data;
//放行
next();
});
};
到这一步我们的后台服务就形成了完成的闭环。
七、服务启动
npm i nodemon
使用 Nodemon 热重载
json
// package.json
{
"scripts": {
"start": "nodemon ./bin/www"
}
}
npm start
八、接口功能验证
我们通过接口管理工具apifox来验证下
先调注册接口
登录接口
请求业务接口

到这一步我们就大功告成了。通过这个项目应该可以让你理解后端开发的基本流程,同时也为前端项目提供了可扩展的 API 支持,后端学习及软件开发的学习的路没有终点,当然我们探索的脚步也不会停下!