首页 / 资讯中心 / 文章详情

Express 使用 MongoDB 数据库:从连接配置到 CRUD 接口的完整落地

Express 使用 MongoDB 数据库:从连接配置到 CRUD 接口的完整落地 ★ FEATURED ARTICLE
1. Express 连接 MongoDB 的工程化落地从连接串到 CRUD 接口Express 是一个轻量的 Node.js Web 框架MongoDB 是文档型数据库两者组合几乎是 Node 后端入门的标配。但很多人第一次写的时候习惯把连接串、Schema、路由全塞进app.js跑通一个 demo 没问题一旦要加第二个集合、要区分开发和生产环境代码就开始互相打架。这篇内容聚焦的就是「工程化路径」怎么把连接配置、模型定义、增删改查接口拆开让项目能持续往上加功能而不是推倒重来。适合谁看已经能用 Express 起一个Hello World但还没系统接过数据库的开发者或者接过一次但连接串硬编码、模型和路由混在一起想重新整理一遍的人。下面给出的目录结构、依赖清单、.env配置和 curl 验证命令都可以直接复制按顺序走一遍你能得到一个能跑、能查、能改、能删的最小可用后端。我试过把连接逻辑写在路由文件里结果每次热重载都会新建一个连接本地调试时连接数一路涨到 MongoDB 的连接上限报错信息还特别隐晦。所以第一步就是把连接做成单例只初始化一次。2. TaoToken 前置准备模型调用与 API Key 管理在写 CRUD 之前先把「模型调用」这条链路准备好。现在很多 Express 项目不只是存数据还要在接口里调用大模型做摘要、分类或者对话。TaoToken 提供统一的模型调用入口你可以在一个地方管理 Key不用在代码里散落多个平台的密钥。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。注册之后进控制台创建 API Key路径是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite Key 列表在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。如果你只是想先验证模型能不能通可以用模型对话页面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 直接发一条消息确认返回正常再写进代码。这里要强调一个工程习惯API Key 和 MongoDB 连接串一样都不能写进代码提交到仓库。两者都放.env用dotenv加载。很多新手只记得藏数据库密码却把模型 Key 直接写在app.js里推到公开仓库后被人刷额度这种事每年都能见到。如果你打算长期做编码类项目或者要接 Agent 工作流可以看下 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它更适合持续性的开发场景。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有 Base URL、鉴权方式和请求示例写代码前扫一遍能省不少调试时间。需要提醒的是TaoToken 是模型调用的统一入口不是数据库也不替代 Express 本身。MongoDB 的连接、Schema、CRUD 还是按下面的步骤来写两者是并行的两条线一条管数据存储一条管模型能力。3. 可复制配置目录结构、依赖与 .env先看最终要得到的目录结构这是整篇的骨架express-mongo-demo/ ├── .env ├── .gitignore ├── app.js ├── package.json ├── config/ │ └── db.js ├── models/ │ └── User.js ├── routes/ │ └── users.js └── controllers/ └── userController.js依赖清单很克制核心就三个npm init -y npm install express mongoose dotenv npm install --save-dev nodemonexpress是 Web 框架mongoose是 MongoDB 的 ODMdotenv负责加载环境变量nodemon只在开发时用来自动重启。装完之后在package.json里加一条启动脚本{ scripts: { dev: nodemon app.js, start: node app.js } }.env文件放敏感配置注意它必须被.gitignore忽略# .env PORT3000 MONGO_URImongodb://127.0.0.1:27017/express_demo TAOTOKEN_API_KEYsk-你的key TAOTOKEN_BASE_URLhttps://taotoken.net/api.gitignore至少包含这几行node_modules/ .env *.log连接逻辑单独放config/db.js做成一个函数只在启动时调用一次// config/db.js const mongoose require(mongoose); async function connectDB() { try { await mongoose.connect(process.env.MONGO_URI); console.log(MongoDB 连接成功); } catch (err) { console.error(MongoDB 连接失败:, err.message); process.exit(1); } } module.exports connectDB;模型定义放models/User.jsSchema 和 Model 都在这里路由不再碰数据库结构// models/User.js const mongoose require(mongoose); const userSchema new mongoose.Schema( { username: { type: String, required: true, unique: true }, email: { type: String, required: true }, age: { type: Number, default: 0 } }, { timestamps: true } ); module.exports mongoose.model(User, userSchema);注意mongoose.model(User, userSchema)会自动把集合名变成复数users这是很多人第一次查不到数据的原因——手动建了user集合代码却在查users。控制器controllers/userController.js封装增删改查// controllers/userController.js const User require(../models/User); exports.createUser async (req, res) { try { const user await User.create(req.body); res.status(201).json({ ok: true, data: user }); } catch (err) { res.status(400).json({ ok: false, message: err.message }); } }; exports.listUsers async (req, res) { const users await User.find().sort({ createdAt: -1 }); res.json({ ok: true, data: users }); }; exports.getUser async (req, res) { const user await User.findById(req.params.id); if (!user) return res.status(404).json({ ok: false, message: not found }); res.json({ ok: true, data: user }); }; exports.updateUser async (req, res) { const user await User.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true }); if (!user) return res.status(404).json({ ok: false, message: not found }); res.json({ ok: true, data: user }); }; exports.deleteUser async (req, res) { const user await User.findByIdAndDelete(req.params.id); if (!user) return res.status(404).json({ ok: false, message: not found }); res.json({ ok: true, message: deleted }); };路由routes/users.js只做路径映射// routes/users.js const express require(express); const router express.Router(); const ctrl require(../controllers/userController); router.post(/, ctrl.createUser); router.get(/, ctrl.listUsers); router.get(/:id, ctrl.getUser); router.put(/:id, ctrl.updateUser); router.delete(/:id, ctrl.deleteUser); module.exports router;最后app.js把上面这些串起来// app.js require(dotenv).config(); const express require(express); const connectDB require(./config/db); const userRoutes require(./routes/users); const app express(); app.use(express.json()); app.use(/api/users, userRoutes); const PORT process.env.PORT || 3000; connectDB().then(() { app.listen(PORT, () console.log(服务已启动: http://localhost:${PORT})); });这套结构的关键点是app.js不出现任何 Schema 和查询语句routes不出现数据库操作models不出现 HTTP 相关代码。职责分开之后加一个新集合只需要复制modelscontrollersroutes三份文件不用动主入口。4. 验证请求curl 逐条测试 CRUD 接口服务启动前确认本地 MongoDB 已经在跑。如果你用 Docker一条命令就够docker run -d --name mongo-dev -p 27017:27017 mongo:7然后启动 Expressnpm run dev看到MongoDB 连接成功和服务已启动两行日志说明连接和监听都正常。接下来逐条验证接口。新增一条用户用 POSTcurl -X POST http://localhost:3000/api/users \ -H Content-Type: application/json \ -d {username:alice,email:aliceexample.com,age:24}预期返回 201body 里带_id、createdAt、updatedAt{ok:true,data:{username:alice,email:aliceexample.com,age:24,_id:...,createdAt:...,updatedAt:...}}查询列表用 GETcurl http://localhost:3000/api/users预期返回{ok:true,data:[...]}数组里能看到刚才那条。如果返回空数组先检查集合名是不是users再检查是不是连到了另一个数据库。按 ID 查单条把上一步返回的_id填进去curl http://localhost:3000/api/users/你的_id更新用 PUTcurl -X PUT http://localhost:3000/api/users/你的_id \ -H Content-Type: application/json \ -d {age:25}预期返回的data.age变成 25updatedAt也刷新了。这里new: true很关键不加的话返回的是更新前的旧文档容易误判成没生效。删除用 DELETEcurl -X DELETE http://localhost:3000/api/users/你的_id预期返回{ok:true,message:deleted}。再查一次列表确认这条已经不在。如果你还想在接口里接模型调用可以在控制器里加一段用.env里的 Key 发请求const resp await fetch(${process.env.TAOTOKEN_BASE_URL}/v1/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${process.env.TAOTOKEN_API_KEY} }, body: JSON.stringify({ model: 你的模型ID, messages: [{ role: user, content: 给这个用户生成一句欢迎语 }] }) }); const data await resp.json();模型 ID 和可用列表在模型对话页面能查到接入细节看文档页。这样数据存储和模型能力就在同一个 Express 服务里各司其职。5. 常见报错排查401、连接失败与 reading choices实际跑的时候报错基本集中在几个地方下面按真实错误信息对照排查。MongooseServerSelectionError: connect ECONNREFUSED 127.0.0.1:27017这是连不上 MongoDB不是代码问题。先确认 MongoDB 进程在跑docker ps看容器状态或者本地服务是否启动。如果用了 Docker注意容器端口有没有映射到宿主机。连接串里的127.0.0.1在容器内指的是容器自己跨容器要用服务名或宿主机地址。MongooseError: Operationusers.insertOne()buffering timed out after 10000ms这个报错通常出现在连接还没建立就发起了查询。mongoose 默认会缓冲操作但超时后就抛这个。检查connectDB()是不是在app.listen之前 await 了别把连接写成「发了就不管」的异步调用。401 Unauthorized调用模型接口时如果 CRUD 正常但调模型返回 401先检查.env里的TAOTOKEN_API_KEY有没有加载成功。可以在启动日志里打印process.env.TAOTOKEN_API_KEY ? key loaded : key missing但别把 Key 本身打出来。另外确认请求头是Authorization: Bearer sk-xxx少了Bearer前缀也会 401。Cannot read properties of undefined (reading choices)这个错误说明你拿到的响应体不是预期的结构通常是请求失败返回了错误对象但代码直接去读data.choices[0]。正确做法是先判断resp.ok不 ok 就把data打出来看错误信息。常见原因是模型 ID 写错、Base URL 少了/v1、或者请求体字段名不对。CastError: Cast to ObjectId failed for value xxx传进findById的字符串不是合法的 ObjectId。检查 URL 里的:id是不是完整的 24 位十六进制别把用户名当 ID 传进去。ValidationError: User validation failed: username: Pathusernameis requiredSchema 里标了required但请求体没带这个字段。用 curl 时确认-d的 JSON 里有username并且Content-Type是application/json否则express.json()解析不出来req.body是空对象。E11000 duplicate key error collection: express_demo.users index: username_1unique: true生效了插入了重复用户名。要么换一个用户名要么在业务层先查再插。注意唯一索引是异步创建的第一次插入可能还没建好索引重启一次服务再试。排查顺序建议固定下来先看服务日志有没有连接成功再用 curl 打一个最简单的 GET确认路由通了最后才查具体业务逻辑。这样能把问题范围快速缩小到「连接层」「路由层」「数据层」中的某一层。6. 继续往下走把接口接进真实项目到这里一个能跑的 Express MongoDB CRUD 服务就完成了。目录结构、.env、模型、控制器、路由、curl 验证命令都是可复制的你可以直接拿这套骨架去改。接下来如果要继续加功能方向大致有三个加参数校验比如用zod或joi在控制器前拦一道、加分页和筛选find后面接skip和limit、加统一错误处理中间件把try/catch里的res.status收敛到一个地方。模型调用那条线Key 和 Base URL 的管理入口在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 接入方式看 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。如果只是验证某个模型能不能用直接去 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 发一条消息最快。长期做编码和 Agent 的话Coding Plan 在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。最后留一个实用习惯每次改完 Schema先在本地用mongosh连上去看一眼实际集合名和字段别只信代码里的模型名。Mongoose 的复数化和索引创建都是异步的肉眼确认一次比对着报错猜半天快得多。
阅读完成 · 觉得有帮助?
咨询建站