拓冰建站拓冰建站
首页 / 资讯中心 / 正文

egg学习(一):用egg-mongoose连接本地MongoDB的配置骨架与验证

1. 为什么第一次接 MongoDB 总卡在“连不上”这一步如果你刚开始学 egg大概率会遇到这样一个场景项目脚手架跑起来了npm run dev也能看到localhost:7001的欢迎页但一旦想接本地 MongoDB就开始报错——要么是MongooseError: connect ECONNREFUSED 127.0.0.1:27017要么是Cannot read property Student of undefined要么是启动日志里压根看不到数据库连接成功的提示。egg 本身是基于 Koa 封装的企业级 Node.js 框架它把约定优于配置这件事做到了极致目录结构、插件加载、配置合并都有固定套路。egg-mongoose 则是官方生态里用来对接 MongoDB 的插件它做的事情很单纯——把 mongoose 实例挂到app.mongoose上再通过app.model暴露给 controller 和 service 使用。听起来简单但第一次配的时候config.default.js里写什么、plugin.js里要不要显式声明、model 目录怎么命名、启动后怎么确认真的连上了这几个点任何一个没对齐都会让你在“明明照着文档写了却跑不通”的状态里耗掉一晚上。这篇内容聚焦的就是这个落地场景从零搭一个 egg 项目装上 egg-mongoose把本地 MongoDB 的连接配置写进config.default.js在plugin.js里启用插件按约定建好 model 目录最后用一个最小的读写请求验证连接是否成功并且能在终端日志里看到明确反馈。目标很直接——你复制配置、改一下数据库名就能跑通一次完整的写入和查询。适合谁看刚接触 egg、本地已经装好 MongoDB、想用 mongoose 做数据层但不想在配置环节反复试错的人。下面所有步骤都是可复制的命令和配置我会给全踩过的坑也会标出来。2. 前置准备TaoToken 与本地环境确认在动 egg 之前先把两件事确认好不然后面报错会分不清是环境问题还是配置问题。第一件是本地 MongoDB 是否真的在跑。macOS 上用brew services list看 mongodb-community 的状态Windows 上在服务里找 MongoDB ServerLinux 用systemctl status mongod。更直接的验证方式是开一个终端执行mongosh --eval db.runCommand({ ping: 1 })返回{ ok: 1 }就说明数据库活着。如果这一步就失败先去把 MongoDB 服务起起来别往下走。第二件是 egg 脚手架。全局装一次就行npm i egg-init -g然后初始化项目这里用 simple 模板够用且干净egg-init egg-mongo-demo --typesimple cd egg-mongo-demo npm i装完先跑一次npm run dev看到egg started on http://127.0.0.1:7001说明骨架没问题。这时候再装 egg-mongoosenpm install egg-mongoose -S关于 TaoToken如果你后续想用统一的 API Key 管理来调试模型对话或者跑 coding plan可以把它当成一个入口官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。不过这一篇的重点是本地 MongoDB 连接TaoToken 的部分放在最后 CTA 里说现在先把数据库跑通。注意egg-mongoose 装完后不要急着改代码先确认package.json的 dependencies 里出现了egg-mongoose版本号记一下后面排查兼容性问题会用到。3. 可复制配置plugin.js 与 config.default.js 骨架egg 的插件机制是这样的config/plugin.js决定“要不要启用某个插件”config/config.default.js决定“这个插件用什么参数”。egg-mongoose 两个文件都要动缺一个都会导致app.mongoose是 undefined。先改config/plugin.js加上use strict; exports.mongoose { enable: true, package: egg-mongoose, };这里enable: true是必须的simple 模板默认不会帮你开。package的值就是 npm 包名别写错。然后改config/config.default.js。原始文件里有一堆默认配置你只需要在return config之前插入 mongoose 段use strict; module.exports appInfo { const config exports {}; config.keys appInfo.name _1690000000000_1234; config.middleware []; // egg-mongoose 连接配置 config.mongoose { url: mongodb://127.0.0.1:27017/egg_mongo_demo, options: { useNewUrlParser: true, useUnifiedTopology: true, }, }; return config; };几个参数说明一下。url里的egg_mongo_demo是数据库名MongoDB 在没有这个库的时候会在第一次写入时自动创建所以不用提前手动建。options里那两个参数在新版 mongoose 里其实已经默认开启但显式写上去能避免不同版本之间的行为差异尤其是你本地 mongoose 版本和 egg-mongoose 依赖的版本不一致时。如果你本地 MongoDB 开了认证url 要写成mongodb://用户名:密码127.0.0.1:27017/egg_mongo_demo?authSourceadminauthSource指向存用户信息的库通常是 admin。没开认证就保持上面那样。提示数据库名不要用中文、不要带空格用下划线分隔最稳。我第一次用mongoTest这种驼峰命名在 mongoose 的某些版本里会被转成小写导致连的库和你以为的不是同一个。配置写完后egg 会在启动时自动加载插件并建立连接。但这时候还没有 modelapp.model是空的所以下一步要建 model 目录。4. 模型目录约定与一次读写验证egg 的约定是model 文件放在app/model/下文件名小写导出一个函数函数接收app参数返回 mongoose model。这个 model 会自动挂到app.model上命名规则是文件名首字母大写。比如app/model/student.js对应app.model.Student。先建文件app/model/student.jsuse strict; module.exports app { const mongoose app.mongoose; const Schema mongoose.Schema; const StudentSchema new Schema({ name: { type: String, required: true }, age: { type: Number, default: 0 }, gender: { type: String, enum: [男, 女] }, createdAt: { type: Date, default: Date.now }, }); return mongoose.model(Student, StudentSchema, student_info); };第三个参数student_info是实际写入 MongoDB 的集合名。如果不写mongoose 会把Student转成复数students作为集合名。我习惯显式指定避免以后查数据时找不到表。接着建 serviceapp/service/student.jsuse strict; const Service require(egg).Service; class StudentService extends Service { async list() { return this.ctx.model.Student.find({}); } async add(payload) { try { const res await this.ctx.model.Student.create(payload); return { success: true, data: res, code: 0 }; } catch (err) { return { success: false, err: err.message, code: -1 }; } } } module.exports StudentService;再建 controllerapp/controller/student.jsuse strict; const Controller require(egg).Controller; class StudentController extends Controller { async list() { const ctx this.ctx; ctx.body await ctx.service.student.list(); } async add() { const ctx this.ctx; const payload ctx.request.body; ctx.body await ctx.service.student.add(payload); } } module.exports StudentController;最后配路由app/router.jsuse strict; module.exports app { const { router, controller } app; router.get(/student/list, controller.student.list); router.post(/student/add, controller.student.add); };启动项目npm run dev如果配置正确终端里会看到 egg 的启动日志并且没有 mongoose 报错。这时候用 curl 发一个写入请求curl -X POST http://127.0.0.1:7001/student/add \ -H Content-Type: application/json \ -d {name:张三,age:20,gender:男}返回{success:true,data:{...},code:0}就说明写入成功。再查一次curl http://127.0.0.1:7001/student/list能看到刚才写入的那条数据整个链路就通了。这时候打开 MongoDB Compass连上mongodb://127.0.0.1:27017在egg_mongo_demo库里找到student_info集合数据应该在里面。注意POST 请求如果报missing csrf token是因为 egg 默认开了 CSRF 防护。本地调试阶段可以在config.default.js里加config.security { csrf: { enable: false } };上线前再按需开启。5. 本篇常见错排查连接阶段的报错基本集中在下面几种按出现频率排ECONNREFUSED 127.0.0.1:27017MongoDB 没启动或者端口不是 27017。先用mongosh确认能连上再检查config.mongoose.url里的端口。app.mongoose is undefinedconfig/plugin.js里没启用 egg-mongoose或者package名写错了。检查plugin.js的exports.mongoose段确认enable: true。app.model.Student is undefinedmodel 文件没放在app/model/下或者文件名和调用名对不上。app/model/student.js对应app.model.Student首字母大写是约定。MongooseError: model already compiled热重载时 model 被重复注册。egg 的 dev 模式会监听文件变化如果你在 model 文件里用了mongoose.model(Student, ...)而没有先判断是否已存在就会报这个。用mongoose.models.Student || mongoose.model(Student, ...)可以规避但正常情况下 egg-mongoose 会处理好出现这个多半是手动引入了 mongoose 实例。写入成功但 Compass 里看不到数据集合名不对。检查 model 第三个参数或者直接在 Compass 里刷新一下有时候需要手动刷新才显示新集合。ValidationError: gender is not a valid enum value写入的 gender 不在[男,女]里。这是 model 层校验生效了说明配置是对的改数据就行。DeprecationWarning: useNewUrlParsermongoose 版本较新时这两个参数已经废弃警告不影响运行想消掉就把options里的这两个键删掉。排查顺序建议先看终端启动日志有没有 mongoose 相关报错再用mongosh确认数据库活着然后检查plugin.js和config.default.js最后看 model 命名。大部分问题在前两步就能定位。6. 跑通之后把调试入口固定下来本地读写跑通只是第一步。实际开发里你会反复做三件事改 model 后重启验证、用 Postman 或 curl 测接口、查数据库里的数据对不对。把这三件事的入口固定下来能省很多时间。接口调试我习惯用 curl 存成脚本或者用 Postman 建一个 collection把add和list两个请求存进去环境变量设成http://127.0.0.1:7001。数据库查看用 MongoDB Compass 连本地收藏egg_mongo_demo这个库每次启动项目后直接点开看。如果你后面要接模型对话或者跑 coding plan 来辅助写 egg 的 service 层逻辑可以用 TaoToken 的 API Key 管理入口https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 模型对话入口是 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。长期做编码和 Agent 的话coding plan 在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。这些和本地 MongoDB 连接是两条线但调试阶段有个顺手的模型入口写 model 校验逻辑和排错会快一些。最后留一个实用技巧在config.default.js里把config.mongoose.options加上debug: truemongoose 会把所有实际执行的数据库操作打印到终端。第一次接的时候打开它你能清楚看到find和create到底发了什么、返回了什么比猜快得多。跑通之后关掉就行不然日志会刷屏。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门