Egg 项目接入 egg-mongoose:从 config.default.js 配置到常用 Mongoose 方法实战

发布时间:2026/9/27 11:39:33
Egg 项目接入 egg-mongoose:从 config.default.js 配置到常用 Mongoose 方法实战 1. Egg 项目里接 MongoDB为什么绕不开 egg-mongoose如果你正在用 Egg 写接口数据又要落到 MongoDB那 egg-mongoose 基本是默认选项。它把 Mongoose 的 Schema、Model、连接管理都挂到 Egg 的 app 和 ctx 上你不用在每个 Service 里手动 require mongoose也不用自己维护连接池。Egg 启动时会按 config 里的配置连库请求进来后直接this.ctx.model.Article就能用这对写业务代码的人来说省掉了很多样板。这篇面向的是已经有一个能跑起来的 Egg 项目、但还没接数据库或者接了但只会find一个方法的同学。我会从config.default.js的配置骨架开始把 model 目录约定、常用增删改查、聚合、分页排序都走一遍最后给一个能本地验证的请求示例。目标很明确照着做完你能在本地跑通一次完整的增删改查闭环而不是只停留在“装了个插件”。需要说明的是Mongoose 本身是操作 MongoDB 的 ODMegg-mongoose 是 Egg 生态里的封装插件。两者版本要匹配Egg 2.x 和 Egg 3.x 在插件加载方式上略有差异下面配置以 Egg 3.x 为主2.x 我会在排障部分点出来。2. 前置准备装插件、开插件、连上库2.1 安装 egg-mongoose在项目根目录执行npm install egg-mongoose --save如果你用的是 pnpm 或 yarn对应替换即可。装完后确认package.json的 dependencies 里有egg-mongoose版本号不用背能装上就说明源没问题。2.2 在 plugin.js 里启用插件Egg 的插件默认不生效必须在config/plugin.js里显式开启// config/plugin.js exports.mongoose { enable: true, package: egg-mongoose, };这一步很多人会漏结果启动时报ctx.model is undefined其实不是配置错是插件根本没加载。2.3 在 config.default.js 写连接配置连接信息放在config/config.default.js里推荐用环境变量兜底本地开发直接连本机// config/config.default.js module.exports appInfo { const config exports {}; config.mongoose { url: process.env.EGG_MONGODB_URL || mongodb://127.0.0.1:27017/website, options: { serverSelectionTimeoutMS: 5000, maxPoolSize: 40, }, }; return config; };这里有两个点值得说。第一url里带库名websiteMongoose 会自动切到这个库不用额外写dbName。第二maxPoolSize是连接池上限老版本 Mongoose 写的是poolSize新版本已经改成maxPoolSize如果你照旧教程写poolSize会看到弃用警告功能还能用但不建议。注意本地 MongoDB 默认端口是 27017如果你用 Docker 起库记得把端口映射出来否则连接会超时。2.4 model 目录约定Egg 约定 model 放在app/model/下文件名就是 model 名的小写。比如app/model/article.js对应app.model.Article。这个约定不能改改了加载不到。// app/model/article.js use strict; module.exports app { const mongoose app.mongoose; const Schema mongoose.Schema; const ArticleSchema new Schema({ title: { type: String, required: true }, keywords: { type: String }, sort: { type: Number, default: 0 }, isSetTop: { type: Number, default: 0 }, release: { type: Boolean, default: false }, columnId: { type: Schema.Types.ObjectId }, tags: { type: Array }, updateTime: { type: Date, default: Date.now }, }); return mongoose.model(Article, ArticleSchema); };Schema 里type支持 String、Number、Boolean、Date、ObjectId、Array、Object 等。ObjectId常用于关联其他集合的_idArray适合标签这类多值字段。default和required是常用约束能省掉不少业务层校验。3. 可复制配置Service 里怎么写增删改查Egg 里操作数据库一般放在 ServiceController 只做参数校验和响应。下面按 create、find、updateOne、aggregate 四类给示例。3.1 新增create// app/service/article.js const Service require(egg).Service; class ArticleService extends Service { async createArticle(payload) { const { ctx } this; const result await ctx.model.Article.create({ title: payload.title, keywords: payload.keywords, sort: payload.sort || 0, tags: payload.tags || [], }); return result; } } module.exports ArticleService;create返回的是插入后的文档对象里面带_id。如果你传的是数组它会批量插入并返回数组。3.2 查询find 与条件查询async listArticles(query) { const { ctx } this; const conditions {}; if (query.title) { conditions.title new RegExp(query.title, i); } if (query.minSort ! undefined) { conditions.sort { $gte: Number(query.minSort) }; } return ctx.model.Article.find(conditions) .sort({ isSetTop: -1, sort: 1, updateTime: -1 }) .skip(Number(query.pageSize) * (Number(query.pageNum) - 1)) .limit(Number(query.pageSize)) .lean(); }这里用了几个常用操作符$gte大于等于$lt小于$ne不等于$in匹配多个值$exists判断字段是否存在。正则用new RegExp比字面量更灵活能动态拼关键词。.lean()返回纯 JS 对象而不是 Mongoose Document读多写少的列表接口用它性能更好。3.3 更新updateOne 与更新修改器async updateArticle(id, payload) { const { ctx } this; return ctx.model.Article.updateOne( { _id: id }, { $set: { title: payload.title, updateTime: new Date() }, $inc: { sort: 1 }, } ); }updateOne返回{ acknowledged, modifiedCount, matchedCount }modifiedCount为 0 说明条件没匹配到或者值没变化。常用修改器有$set设置值、$inc自增、$unset删字段、$push往数组加元素、$pull从数组删元素、$addToSet去重添加。数组批量插入可以配合$each。3.4 聚合aggregate统计每个栏目的文章数async countByColumn() { const { ctx } this; return ctx.model.Article.aggregate([ { $match: { release: true } }, { $group: { _id: $columnId, total: { $sum: 1 } } }, { $sort: { total: -1 } }, ]); }aggregate返回的是普通数组不走 Mongoose 的 Document 包装。$match相当于 where$group做分组$sum累加。聚合管道写起来像流水线每一步的输出是下一步的输入复杂统计基本都能拼出来。4. 验证请求本地跑通一次闭环配置和 Service 写完后加一个 Controller 和路由用 curl 验证。// app/controller/article.js const Controller require(egg).Controller; class ArticleController extends Controller { async create() { const { ctx } this; const body ctx.request.body; const result await ctx.service.article.createArticle(body); ctx.body { code: 0, data: result }; } async list() { const { ctx } this; const list await ctx.service.article.listArticles(ctx.query); ctx.body { code: 0, data: list }; } } module.exports ArticleController;// app/router.js module.exports app { const { router, controller } app; router.post(/api/article, controller.article.create); router.get(/api/article, controller.article.list); };启动项目npm run dev先插一条curl -X POST http://127.0.0.1:7001/api/article \ -H Content-Type: application/json \ -d {title:Egg接入Mongoose,keywords:egg,mongoose,sort:10,tags:[node,egg]}返回里应该能看到_id和title。再查列表curl http://127.0.0.1:7001/api/article?pageNum1pageSize5minSort5如果返回数组里有刚才那条说明连接、model、Service、路由整条链路都通了。这一步别跳过很多人配置写完不验证等到业务报错才回头查成本更高。5. 本篇常见错排查报错ctx.model is undefined九成是config/plugin.js没开插件或者 model 文件没放在app/model/下。Egg 是按目录约定加载的路径错了不会报文件不存在只会静默不注册。连接超时MongooseServerSelectionError先确认 MongoDB 进程在跑mongosh能连上再确认url里的 host 和端口对。Docker 场景常见的是容器内用了127.0.0.1应该换成容器名或宿主机 IP。poolSize弃用警告Mongoose 6 以后连接池参数改成maxPoolSize把options.server.poolSize换成顶层maxPoolSize即可。updateOne返回modifiedCount: 0要么条件没匹配到要么新值和旧值一样。可以先用findOne确认数据存在再检查$set的字段是否真的变了。Egg 2.x 与 3.x 差异2.x 的插件配置和 model 加载基本一致但部分中间件和appInfo用法不同。如果你从 2.x 升级重点看config.default.js的导出方式3.x 推荐module.exports appInfo {}。聚合结果为空$match里的字段名要带$前缀引用文档字段比如$columnId写成columnId会被当成字符串常量结果自然不对。6. 把模型调用接到稳定的 API 通道上本地跑通之后下一步通常是把这些 model 调用接到真实的模型服务或业务接口上。这时候请求的稳定性和 Key 管理就变得重要。我一般会把模型调用统一走 TaoToken 的 API 通道地址是 https://taotoken.net/api Key 在控制台生成接入文档里有各语言的示例照着改 base_url 就行。如果你只是先验证模型返回可以直接用模型对话页面试一条请求确认通道通了再写进代码。长期做编码或 Agent 类项目的话Coding Plan 更适合额度和调用方式都按开发场景设计。控制台里可以管理 API Keys接入文档在 https://taotoken.net/doc ClaudeCodeAnthropic 相关的配置也有单独说明。把这些前置动作做完再回到 Egg 的 Service 里发请求排障时就能分清是数据库层的问题还是模型通道的问题定位会快很多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询