ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

egg3.0 连接 egg-mongoose 插入一条数据、插入多条数据:config 骨架与验证动作

egg3.0 连接 egg-mongoose 插入一条数据、插入多条数据:config 骨架与验证动作 1. egg3.0 里把 egg-mongoose 跑通到底卡在哪如果你正在用 egg3.0 写后端想让 Node.js 服务直接操作 MongoDBegg-mongoose基本是最省心的选择。它把 mongoose 的 Model 挂到ctx.model上你在 controller 和 service 里不用自己维护连接直接ctx.model.Xxx.create()就能写数据。听起来简单但实际落地时很多人第一步就卡住插件装了、config 写了启动却报Cannot read properties of undefined (reading Role)或者插入成功但返回的results是undefined接口永远返回 fail。这篇就聚焦一件事在 egg3.0 项目里用 egg-mongoose 完成插入一条数据和插入多条数据把 config 骨架、model 定义、service 调用、controller 返回、结果验证这一整条链路跑通。适合已经会写 Egg 基础路由、但对 mongoose 接入还不熟的后端开发者。下面所有代码都可以直接复制进项目改改就用我会把每个文件放哪、为什么这么写、验证时看什么都讲清楚。先说结论egg-mongoose 的接入分四块——装依赖、开插件、配 config、写 model。插入数据则分两条路单条用create()多条用insertMany()。真正容易出错的不是 API 本身而是 model 文件名和ctx.model后面的名字对不上、以及 service 里把异步结果吞掉了。2. 前置准备装依赖、开插件、配 config.default.js2.1 安装 egg-mongoose在项目根目录执行npm i egg-mongoose --saveegg3.0 对应的是 egg-mongoose 的较新版本装完确认package.json里有这条依赖即可。注意不要装成老的egg-mongoose低版本否则可能和 egg3 的插件加载机制不兼容。2.2 开启插件 plugin.js在config/plugin.js里显式开启别指望它自动加载use strict; exports.mongoose { enable: true, package: egg-mongoose, };这里exports.mongoose的 key 名就是插件名egg 会按这个去node_modules找egg-mongoose。如果你写成exports.mongo插件照样能加载但 config 里的配置段名要跟着改容易乱建议统一用mongoose。2.3 配置 config.default.js这是最容易写错的地方。骨架如下use strict; module.exports appInfo { const config exports {}; config.keys appInfo.name _1700000000000_0000; // 关闭 CSRF方便本地用 Postman 直接 POST 验证 config.security { csrf: { enable: false, }, }; // egg-mongoose 连接配置 config.mongoose { client: { url: mongodb://127.0.0.1:27017/egg_demo, options: { useUnifiedTopology: true, }, // 挂载到 app 上方便调试 plugins: [], }, }; return config; };几个关键点。url里的egg_demo是数据库名MongoDB 不需要提前建库第一次写入会自动创建。options里useUnifiedTopology在新版驱动里其实已经默认但写上不报错属于保险。config.keys那串随便改只要够长够随机cookie 签名用。注意如果你本地 MongoDB 没启动或者端口不是 27017这里要同步改。启动 Egg 时如果连不上日志会打印Mongoose connection error别忽略。2.4 目录结构确认egg-mongoose 默认约定 model 放在app/model/下文件名小写导出的是一个函数。最终结构大概是这样app/ ├── controller/ │ └── role.js ├── model/ │ └── role.js ├── service/ │ └── role.js └── router.js config/ ├── config.default.js └── plugin.jsmodel 目录名必须是model不能叫models否则 egg-mongoose 扫不到。3. 可复制配置model、service、controller、router 全套3.1 定义 modelapp/model/role.jsuse strict; module.exports app { const mongoose app.mongoose; const Schema mongoose.Schema; const RoleSchema new Schema({ name: { type: String }, id: { type: String }, }); // 第一个参数是 Model 名第二个是 Schema第三个是集合名 return mongoose.model(Role, RoleSchema, user); };这里mongoose.model(Role, ...)的第一个参数Role决定了你在ctx.model.Role里怎么取。第三个参数user是 MongoDB 里真实的集合名不写的话默认会变成roles复数小写。我特意写成user是为了让你看到「Model 名」和「集合名」可以不一样别被绕晕。3.2 service 里写单条插入app/service/role.jsuse strict; const Service require(egg).Service; class RoleService extends Service { // 插入一条 async addItem() { const { ctx } this; try { const res await ctx.model.Role.create({ name: test-SA, id: 20230424112112, }); console.log(single insert result:, res); return { ok: true, data: res }; } catch (err) { ctx.logger.error(single insert error:, err); return { ok: false, msg: err.message }; } } } module.exports RoleService;对比一下原始写法里用.then().catch()把结果转成布尔值的做法我更推荐直接await拿到插入后的文档对象。因为create()返回的是刚写入的那条记录包含_id前端拿到能直接用。用布尔值的话你只知道成功失败拿不到主键后面想跳转详情页还得再查一次。3.3 service 里写批量插入同一个 service 文件里加一个方法// 插入多条 async batchAdd() { const { ctx } this; try { const list [ { name: 木子, id: 1001 }, { name: 木子1, id: 1002 }, { name: 木子2, id: 1003 }, { name: 木子3, id: 1004 }, ]; const res await ctx.model.Role.insertMany(list); console.log(batch insert result:, res); return { ok: true, count: res.length, data: res }; } catch (err) { ctx.logger.error(batch insert error:, err); return { ok: false, msg: err.message }; } }insertMany()接收数组返回写入成功的文档数组。默认情况下它是「有序插入」遇到某条出错会中断前面的已经写进去了。如果你希望出错也继续插可以传第二个参数{ ordered: false }await ctx.model.Role.insertMany(list, { ordered: false });这个参数在批量导入场景很实用比如一批数据里有几条格式不对你不想因为一条坏数据丢掉整批。3.4 controller 暴露接口app/controller/role.jsuse strict; const { Controller } require(egg); class RoleController extends Controller { // 插入一条 async addUser() { const { ctx } this; const res await ctx.service.role.addItem(); ctx.body { code: res.ok ? 0 : 4001, msg: res.ok ? success : res.msg, data: res.data || null, }; } // 插入多条 async batchAdd() { const { ctx } this; const res await ctx.service.role.batchAdd(); ctx.body { code: res.ok ? 0 : 4001, msg: res.ok ? success : res.msg, count: res.count || 0, }; } } module.exports RoleController;3.5 router 注册路由app/router.jsuse strict; module.exports app { const { router, controller } app; router.post(/role/add, controller.role.addUser); router.post(/role/batchAdd, controller.role.batchAdd); };用 POST 而不是 GET因为写操作不该走 GET。本地验证时用 Postman 或 curl 都行。4. 验证请求看日志、看返回、看数据库4.1 启动项目npm run dev看到egg started on http://127.0.0.1:7001就说明起来了。如果这里报 mongoose 连接错误先回去检查 MongoDB 是否启动、url 是否正确。4.2 验证单条插入curl -X POST http://127.0.0.1:7001/role/add预期返回{ code: 0, msg: success, data: { name: test-SA, id: 20230424112112, _id: 6650f1a2c9e77b0012a3b4c5, __v: 0 } }同时终端会打印single insert result:和那条文档。_id是 MongoDB 自动生成的主键__v是 mongoose 的版本字段正常现象。4.3 验证批量插入curl -X POST http://127.0.0.1:7001/role/batchAdd预期返回{ code: 0, msg: success, count: 4 }终端会打印batch insert result:和四条文档组成的数组。4.4 直接查数据库确认光看接口返回还不够最好进 MongoDB 确认数据真的落库了mongosh use egg_demo db.user.find().pretty()你应该能看到刚才插入的 5 条记录1 条单插 4 条批量。如果db.user是空的但接口返回 success那大概率是 model 里第三个参数集合名写错了数据写到了别的集合用show collections看一眼就知道。5. 本篇常见错排查5.1 ctx.model.Role is undefined最常见。原因通常是 model 文件名和ctx.model.后面的名字对不上。egg-mongoose 会把app/model/role.js里的mongoose.model(Role, ...)注册成ctx.model.Role注意首字母大写。如果你 model 文件叫role.js但里面写的是mongoose.model(role, ...)那就要用ctx.model.role。建议统一文件名小写Model 名首字母大写。5.2 插入成功但返回 undefined原始代码里用.then(res { results true })这种写法如果await的是外层函数而里面没 return就会拿到 undefined。正确做法是const res await ctx.model.Role.create(...)直接拿返回值。别在 await 里套 then。5.3 批量插入报 E11000 duplicate key说明你的 Schema 里某个字段加了unique: true而插入的数据有重复值。检查一下id字段是不是设了唯一索引或者数据库里已经有相同id的记录。批量插入时只要有一条重复默认整个操作会中断。5.4 连接超时 / Mongoose connection error先确认 MongoDB 服务在跑mongosh能连上就说明服务没问题。然后检查config.mongoose.client.url里的 host 和端口。如果你用的是远程数据库还要确认网络可达。本地开发最常见的就是忘了启动 MongoDB。5.5 集合名不对数据写到了别的地方mongoose.model(Role, RoleSchema, user)第三个参数才是集合名。不写的话mongoose 会把 Model 名转成复数小写Role变成roles。所以如果你在db.user里找不到数据去db.roles看看。6. 把写入链路接进你的项目到这里egg3.0 egg-mongoose 的单条和批量插入就完整跑通了。回顾一下关键动作plugin.js开插件、config.default.js配连接、app/model/定义 Schema 和 Model、service 里用create()和insertMany()、controller 返回结构化结果、最后用 curl 加 mongosh 双重验证。如果你后面要把这套接入真实的编码工作流比如让 AI 辅助生成 model 和 service 模板可以配合 TaoToken 的模型对话能力来试模型对话入口在 https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。需要长期跑编码任务或 Agent 的话Coding Plan 在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。API Key 在控制台生成https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。最后留一个我踩过的坑批量插入时如果数据量很大别一次性insertMany几万条MongoDB 单次请求有大小限制分批每批 1000 条左右更稳。这个在导入历史数据时特别重要。
返回列表