1. 从一次本地 CRUD 跑不通说起
mongoose 是 Node.js 里操作 MongoDB 最常用的 ODM 库,它把「集合」抽象成 Schema,把「文档」抽象成 Model 和 Entity,让你不用手写原生驱动那套回调也能完成增删改查。这篇面向的是刚接触 Node.js、想在本机把第一个数据模型跑起来的新手:从 npm 安装 mongoose、定义 Schema 与 Model,到本地 CRUD 验证,一条链路走完。适合谁?写过一点 JavaScript、装过 Node、但还没真正让数据落进 MongoDB 的人。
我试过最典型的翻车场景是这样的:npm install mongoose装完,mongoose.connect也写了,脚本一跑却卡在连接上,或者save回调里err一直有值,控制台只丢一句超时。问题往往不在 mongoose 本身,而在连接串、服务是否启动、以及调用凭证散落在各处难管理。所以这篇除了把 Schema 建模和 CRUD 讲透,还会顺带说清楚怎么用 TaoToken 把调用凭证统一管起来,避免以后接模型能力时 Key 到处复制。
下面按「装依赖 → 连库 → 建模 → 增删改查 → 排错」的顺序来,每一步都给可复制的代码,你跟着敲就能在本地看到结果。
2. TaoToken 前置:把调用凭证先收拢
在写业务代码之前,先把凭证这件事理清楚。TaoToken 是一个统一 Key / API 通道的管理入口,官网在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基址是 https://taotoken.net/api 。它的价值在于:你后面不管是接模型对话、跑 coding plan,还是给 Agent 配通道,都不用把一堆 Key 硬编码进app.js。
具体操作路径很直接。先到控制台 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 建好项目,再去 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 生成一把 Key。生成后不要写死在代码里,放进.env:
# .env TAOTOKEN_API_KEY=你的Key MONGODB_URI=mongodb://127.0.0.1:27017/mongoose_demo注意:
.env一定要进.gitignore,凭证进仓库是新手最容易犯的错。
如果你只是想先验证模型能不能通,可以直接用模型对话页 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite 试一条请求,确认 Key 有效再写进项目。长期要跑编码或 Agent 任务的话,Coding Plan 页面 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 里有对应的套餐说明,按需选就行。接入细节和参数以接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 为准,别凭记忆猜字段。
这一步做完,你手里应该有两样东西:一把可用的 TaoToken Key,一个本地 MongoDB 连接串。接下来进入正题。
3. 可复制配置:依赖、连接与 Schema 建模
3.1 package.json 依赖片段
先初始化项目,装 mongoose 和 dotenv:
npm init -y npm install mongoose dotenv装完package.json的依赖部分大概长这样,版本号以你实际安装为准:
{ "name": "mongoose-demo", "version": "1.0.0", "type": "commonjs", "scripts": { "start": "node app.js" }, "dependencies": { "dotenv": "^16.4.5", "mongoose": "^8.5.0" } }mongoose 8.x 默认已经支持 Promise,不需要再手动mongoose.Promise = global.Promise,老教程里那行可以省掉。
3.2 连接配置骨架
新建db.js,把连接逻辑单独抽出来,方便复用:
// db.js const mongoose = require("mongoose"); require("dotenv").config(); async function connectDB() { try { await mongoose.connect(process.env.MONGODB_URI, { serverSelectionTimeoutMS: 5000, }); console.log("MongoDB 连接成功"); } catch (err) { console.error("MongoDB 连接失败:", err.message); process.exit(1); } } module.exports = connectDB;serverSelectionTimeoutMS设成 5000,是为了连不上时快速报错,而不是默认等 30 秒。本地开发这个值很实用。
3.3 Schema 与 Model
mongoose 里三个概念要分清:Schema 是骨架,只描述字段结构,不能直接操作数据库;Model 由 Schema 编译而来,具备操作能力;Entity 是 Model 实例化出来的具体文档,用save落库。新建models/user.js:
// models/user.js const mongoose = require("mongoose"); const Schema = mongoose.Schema; const userSchema = new Schema( { username: { type: String, unique: true, required: true }, name: String, password: String, mobile: String, email: String, age: Number, tags: [String], createdAt: { type: Date, default: Date.now }, }, { collection: "users" } ); module.exports = mongoose.model("User", userSchema);字段类型支持字符串、日期、数值、布尔、数组、内嵌文档等。unique: true只是建索引约束,真正生效需要索引建好,第一次插入重复值不一定立刻报错,这点后面排错会讲。
4. 验证请求:一条可执行的 CRUD 脚本
新建app.js,把连接、建模、增删改查串起来:
// app.js const connectDB = require("./db"); const User = require("./models/user"); async function main() { await connectDB(); // 增:创建并保存 const created = await User.create({ username: "alice", name: "Alice", password: "123456", email: "alice@example.com", age: 22, tags: ["node", "mongodb"], }); console.log("新增成功, _id =", created._id.toString()); // 查:条件查询 + 限制数量 + 排序 const list = await User.find({ age: { $gte: 18 } }) .sort({ age: -1 }) .limit(10) .exec(); console.log("查询到", list.length, "条"); // 改:更新一条 const updated = await User.findByIdAndUpdate( created._id, { $set: { name: "Alice Chen" } }, { new: true } ); console.log("更新后 name =", updated.name); // 删:删除一条 const removed = await User.findByIdAndDelete(created._id); console.log("已删除:", removed ? removed.username : "无匹配"); await require("mongoose").disconnect(); } main().catch((err) => { console.error("执行出错:", err); process.exit(1); });跑之前确认本地 MongoDB 已启动。用node app.js执行,正常输出类似:
MongoDB 连接成功 新增成功, _id = 66f1a2b3c4d5e6f7a8b9c0d1 查询到 1 条 更新后 name = Alice Chen 已删除: alice看到这四行,说明 Schema 建模和 CRUD 全通了。几个常用查询操作对照一下:
| 操作 | 写法 | 说明 |
|---|---|---|
| 查全部 | User.find({}) | 不传条件默认返回所有文档 |
| 条件查 | User.find({ age: { $gt: 18 } }) | $gt大于,$lt小于,$gte大于等于 |
| 查一条 | User.findOne({ username: "alice" }) | 只返回第一条匹配 |
| 按 ID 查 | User.findById(id) | 只接收_id,返回单个文档 |
| 限制数量 | User.find({}, null, { limit: 20 }) | 第三个参数是 options |
| 排序 | User.find({}).sort({ age: 1 }) | 1 升序,-1 降序 |
5. 本篇常见错排查
连不上库,报MongooseServerSelectionError。九成是 MongoDB 服务没起,或者连接串端口不对。本地默认27017,先确认服务在跑,再检查.env里的MONGODB_URI有没有写错库名。
save回调里err一直有值。先看err.message,常见是必填字段缺失或类型不匹配。Schema 里required: true的字段没传,插入就会失败。
unique: true没拦住重复。唯一索引是异步建的,第一次插入时索引可能还没建好。可以手动await User.init()等索引建完再插,或者用syncIndexes。
查询返回空数组但数据明明在。检查集合名。mongoose 默认把 Model 名User转成复数users,如果你手动建过user集合就对不上。上面 Schema 里用collection: "users"显式指定,能避免这个坑。
回调风格和 Promise 混用。mongoose 8.x 推荐async/await,老教程里的find(cond, callback)也能用,但别在同一个调用里既传 callback 又await,会拿到 undefined。
凭证管理混乱。如果你后面还要接模型能力,别把 TaoToken Key 和 MongoDB 连接串混在一个文件里硬编码。统一走.env,Key 从 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 生成,接入参数对照接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 核对。
6. 把凭证和模型能力接上
本地 CRUD 跑通只是第一步。真实项目里,你往往还要在 Node 服务里调模型能力,比如给用户输入做摘要、给数据打标签。这时候凭证管理就重要了:MongoDB 连接串管数据,TaoToken Key 管模型调用,两者都放.env,代码里只读环境变量。
验证模型通道是否可用,去模型对话页 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite 发一条测试请求最直观。要长期跑编码或 Agent 任务,Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 里有对应方案。接入时如果遇到鉴权或参数报错,先翻接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,再回 API Keys 页面确认 Key 状态。
一个实用习惯:把connectDB和模型调用封装成独立模块,app.js只负责编排流程。这样以后换库、换通道,改动都集中在一处,不会牵一发动全身。mongoose 的 Schema 建模和 CRUD 是地基,凭证统一管理是让地基能往上盖楼的前提,两件事一起做,后面接什么能力都不慌。