前言
本教程将引导你在 Egg.js 框架中使用 typescript,安装 egg-mongoose 插件连接和操作 MongoDB 数据库,涵盖安装、配置、模型定义、基本 CRUD 操作以及高级功能如聚合查询和关联查询。
https://www.eggjs.org/zh-CN/tutorials/typescript
本示例代码:https://gitee.com/OnlyDawn1122/egg-mongoose-demo
1. 前置条件
- Node.js:确保已安装 Node.js(建议版本 14.x 或更高)。
- MongoDB:本地或云端已安装并运行 MongoDB 数据库。你可以使用 MongoDB Atlas 或本地安装 MongoDB。
- Egg.js 项目:已初始化一个 Egg.js 项目。如果没有,可通过以下命令创建:
mkdir egg-mongoose-demo && cd egg-mongoose-demo npm init egg --type=ts npm i
1.1 本地 Docker 安装 mongodb
docker pull mongo:latest
mongoDB 服务已启动:
Navicat Mongodb 连接皆可
2. 安装 egg-mongoose
2.1 安装依赖
在项目根目录下运行以下命令安装 egg-mongoose
插件:
npm i egg-mongoose --save
2.2 启用插件
在 {app_root}/config/plugin.ts
中启用 egg-mongoose
插件:
// config/plugin.ts
module.exports = {
mongoose: {
enable: true,
package: 'egg-mongoose',
},
};
3. 配置 MongoDB 连接
在 {app_root}/config/config.default.ts
中配置 MongoDB 连接信息。支持单数据库和多数据库配置。
3.1 单数据库配置
// config/config.default.ts
module.exports = appInfo => {
const config = exports = {};
// 配置 mongoose
config.mongoose = {
client: {
url: 'mongodb://127.0.0.1:27017/egg_mongoose_demo', // 替换为你的 MongoDB 地址
options: {
useNewUrlParser: true,
useUnifiedTopology: true,
},
},
};
return {
...config,
};
};
3.2 多数据库配置
如果需要连接多个 MongoDB 数据库,可以这样配置:
// config/config.default.ts
module.exports = appInfo => {
const config = exports = {};
// 配置 mongoose
config.mongoose = {
clients: {
db1: {
url: 'mongodb://127.0.0.1:27017/db1',
options: {
useNewUrlParser: true,
useUnifiedTopology: true,
},
},
db2: {
url: 'mongodb://127.0.0.1:27017/db2',
options: {
useNewUrlParser: true,
useUnifiedTopology: true,
},
},
},
};
return {
...config,
};
};
4. 定义模型(Model)
在 Egg.js 中,模型通常放在 app/model
目录下,用于定义 MongoDB 集合的 Schema 和 Model。
4.1 创建 User 模型
创建一个简单的用户模型,包含 username
和 password
字段:
// app/model/user.ts
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const UserSchema = new Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
status: { type: Number, default: 1 }, // 1: 启用, 0: 禁用
createdAt: { type: Date, default: Date.now },
});
return mongoose.model('User', UserSchema, 'user'); // 第三个参数指定集合名称
};
4.2 创建 Order 模型(用于关联查询)
创建一个订单模型,包含 order_id
和 total_price
字段:
// app/model/order.ts
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const OrderSchema = new Schema({
order_id: { type: String, required: true },
uid: { type: Number },
total_price: { type: Number },
createdAt: { type: Date, default: Date.now },
});
return mongoose.model('Order', OrderSchema, 'order');
};
4.3 多数据库模型定义
如果使用多数据库配置,需指定连接的数据库:
// app/model/user.ts (连接 db1)
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const conn = app.mongooseDB.get('db1'); // 指定数据库连接
const UserSchema = new Schema({
username: { type: String, required: true },
password: { type: String, required: true },
});
return conn.model('User', UserSchema);
};
5. 实现 CRUD 操作
在 Egg.js 中,建议将数据库操作逻辑放在 service
层,控制器(controller
)调用 service
处理业务逻辑。
5.1 创建 Service
创建 app/service/user.ts
来处理用户相关的数据库操作:
// app/service/user.ts
const Service = require('egg').Service;
class UserService extends Service {
// 查询所有用户
async getUserList() {
return await this.ctx.model.User.find({});
}
// 添加用户
async addUser(userData) {
const user = new this.ctx.model.User(userData);
return await user.save();
}
// 更新用户
async updateUser(id, updateData) {
return await this.ctx.model.User.updateOne(
{ _id: id },
{ $set: updateData }
);
}
// 删除用户
async deleteUser(id) {
return await this.ctx.model.User.deleteOne({ _id: id });
}
}
module.exports = UserService;
5.2 创建 Controller
创建 app/controller/user.ts
来处理 HTTP 请求:
// app/controller/user.ts
const Controller = require('egg').Controller;
class UserController extends Controller {
// 获取用户列表
async index() {
const { ctx } = this;
const userList = await ctx.service.user.getUserList();
ctx.body = {
code: 200,
data: userList,
msg: '获取用户列表成功',
};
}
// 添加用户
async create() {
const { ctx } = this;
const userData = ctx.request.body;
const result = await ctx.service.user.addUser(userData);
ctx.body = {
code: 200,
data: result,
msg: '添加用户成功',
};
}
// 更新用户
async update() {
const { ctx } = this;
const { id } = ctx.params;
const updateData = ctx.request.body;
const result = await ctx.service.user.updateUser(id, updateData);
ctx.body = {
code: 200,
data: result,
msg: '更新用户成功',
};
}
// 删除用户
async destroy() {
const { ctx } = this;
const { id } = ctx.params;
const result = await ctx.service.user.deleteUser(id);
ctx.body = {
code: 200,
data: result,
msg: '删除用户成功',
};
}
}
module.exports = UserController;
创建 app/controller/csrf.ts
,返回 CSRF 令牌
// app/controller/csrf.ts
const Controller = require('egg').Controller;
class CsrfController extends Controller {
async get() {
const { ctx } = this;
ctx.body = {
csrfToken: ctx.csrf, // 获取 CSRF 令牌
};
}
}
module.exports = CsrfController;
如果你的应用是 API 服务或正在开发阶段,可以临时禁用 CSRF 保护。
// config/config.default.ts
module.exports = appInfo => {
const config = exports = {};
config.security = {
csrf: {
enable: false, // 禁用 CSRF 保护
},
};
return config;
};
针对特定路由禁用 CSRF:
// app/router.ts
module.exports = app => {
const { router, controller } = app;
router.post('/users', controller.user.create, { csrf: false }); // 禁用 CSRF
};
5.3 配置路由
在 app/router.ts
中定义路由:
// app/router.ts
module.exports = app => {
const { router, controller } = app;
router.get('/csrf', controller.csrf.get); // 获取 CSRF 令牌
router.get('/users', controller.user.index); // 获取用户列表
router.post('/users', controller.user.create); // 添加用户
router.put('/users/:id', controller.user.update); // 更新用户
router.delete('/users/:id', controller.user.destroy); // 删除用户
};
6. 高级功能:关联查询与聚合管道
6.1 关联查询($lookup)
假设有一个 order_item
集合与 order
集合关联,可以使用 $lookup
进行关联查询:
// app/controller/order.ts
const Controller = require('egg').Controller;
class OrderController extends Controller {
async index() {
const { ctx } = this;
const orderResult = await ctx.model.Order.aggregate([
{
$lookup: {
from: 'order_item', // 关联的集合名
localField: 'order_id',
foreignField: 'order_id',
as: 'items',
},
},
{
$match: {
total_price: { $gte: 90 }, // 过滤条件
},
},
]);
ctx.body = {
code: 200,
data: orderResult,
msg: '获取订单列表成功',
};
}
}
module.exports = OrderController;
6.2 分页查询
在 service
层实现分页查询:
// app/service/user.ts
async getUserList(page = 1, pageSize = 10) {
const skip = (page - 1) * pageSize;
const users = await this.ctx.model.User.find({})
.sort({ createdAt: -1 }) // 按创建时间倒序
.skip(skip)
.limit(pageSize);
return users;
}
7. 启动项目
运行以下命令启动 Egg.js 项目:
npm run dev
访问 http://localhost:7001/users
测试 API。可以使用 Postman 或 cURL 发送 POST、PUT、DELETE 请求测试其他功能。
Postman 测试
添加用户
{
"username": "john_doe1",
"password": "password1231",
"status": 1
}
8. 注意事项
- 错误处理:在生产环境中,建议在
service
和controller
中添加 try-catch 块处理异常。 - 安全性:避免在配置文件中硬编码敏感信息(如 MongoDB 密码),可使用环境变量(如
process.env.MONGO_URL
)。 - 性能优化:为常用查询字段添加索引(如
username: { index: true }
)。