1. 从能跑到能改Express Mongoose 中间件骨架到底解决什么问题如果你已经能用 Express 写出几个接口也能用 Mongoose 存几条数据但每次加功能都在app.js里堆代码那这篇笔记就是写给你的。核心检索词先摆出来Express 中间件分层、Mongoose 模型组织、npm 依赖管理、可复用 app.js 骨架。这套骨架能做什么它把「路由、模型、中间件、配置、错误处理」拆成独立文件让你新增一个业务模块时只改两三个文件而不是翻遍整个入口。适合谁适合刚跨过 Node 语法门槛、准备把练手项目往工程化方向推的开发者。我试过最原始的做法所有app.use、所有mongoose.model、所有app.get全塞在一个文件里。项目小的时候没问题一旦接口超过十个改一个字段要上下滚动几百行中间件顺序错了还很难排查。Express 的中间件本质是一条流水线请求从第一个app.use进入按注册顺序依次经过每个函数谁先next()谁就交棒。Mongoose 的模型则是数据层的契约Schema 定义字段和索引Model 负责增删改查。把这两条线理清楚骨架自然就出来了。下面我会先讲依赖怎么组织再给一份可直接复制的app.js与config骨架然后重点说中间件注册顺序最后用 curl 验证整条请求链路和错误处理。全程围绕 Express、Mongoose、npm、中间件这几个热词展开每一步都能跟着敲。2. 前置准备npm 依赖组织与 TaoToken 接入配置2.1 初始化项目与依赖分层先建目录再初始化package.json。依赖不要一股脑npm install按用途分组写进package.json这样别人 clone 下来一眼能看懂项目结构。mkdir express-mongoose-skeleton cd express-mongoose-skeleton npm init -ypackage.json的依赖部分建议这样组织运行时依赖和开发依赖分开{ name: express-mongoose-skeleton, version: 1.0.0, scripts: { start: node app.js, dev: nodemon app.js }, dependencies: { express: ^4.19.2, mongoose: ^8.4.0, dotenv: ^16.4.5, morgan: ^1.10.0 }, devDependencies: { nodemon: ^3.1.0 } }安装命令对应执行npm install express mongoose dotenv morgan npm install -D nodemon这里dotenv用来加载环境变量morgan记录请求日志nodemon只在开发时用。npm 的依赖组织原则很简单生产环境必须有的放dependencies只在本地开发用的放devDependencies。这样部署时npm install --production不会装多余的东西。2.2 TaoToken 接入配置如果你在本地调试时需要调用大模型接口做辅助验证可以通过 TaoToken 统一接入。它的 API 地址是https://taotoken.net/api官网入口在https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content。先把 Key 配到环境变量里不要硬编码进代码。在项目根目录建.env文件# .env PORT3000 MONGO_URImongodb://127.0.0.1:27017/skeleton TAOTOKEN_API_KEY你的Key TAOTOKEN_BASE_URLhttps://taotoken.net/api然后在config里统一读取。这样做的目的是把「配置」和「代码」分离换环境只改.env不动业务逻辑。Key 的获取和模型对话调试可以走模型对话入口长期编码或 Agent 场景可以看 Coding Plan接入细节在接入文档里。3. 可复制配置app.js 与 config 骨架3.1 目录结构先约定目录后面所有代码都按这个结构放express-mongoose-skeleton/ ├── app.js ├── config/ │ └── index.js ├── models/ │ └── user.js ├── routes/ │ └── user.js ├── middlewares/ │ ├── auth.js │ └── errorHandler.js └── .env3.2 config/index.js// config/index.js require(dotenv).config(); module.exports { port: process.env.PORT || 3000, mongoUri: process.env.MONGO_URI || mongodb://127.0.0.1:27017/skeleton, taotoken: { apiKey: process.env.TAOTOKEN_API_KEY, baseUrl: process.env.TAOTOKEN_BASE_URL || https://taotoken.net/api } };3.3 models/user.jsMongoose 模型单独成文件Schema 和 Model 都放这里路由层只调用 Model不直接碰 Schema。// models/user.js const mongoose require(mongoose); const userSchema new mongoose.Schema({ name: { type: String, required: true, unique: true }, email: { type: String, required: true }, createdAt: { type: Date, default: Date.now } }); module.exports mongoose.model(User, userSchema);3.4 middlewares/auth.js 与 errorHandler.js自定义中间件分两类普通中间件三个参数req, res, next错误处理中间件必须四个参数err, req, res, next。这个区别是 Express 识别错误处理器的唯一依据少一个参数就不会被当成错误处理中间件。// middlewares/auth.js module.exports function auth(req, res, next) { const token req.headers[x-auth-token]; if (!token) { const err new Error(缺少认证 token); err.status 401; return next(err); } req.user { name: demo }; next(); };// middlewares/errorHandler.js module.exports function errorHandler(err, req, res, next) { const status err.status || 500; res.status(status).json({ code: status, message: err.message || 服务器内部错误 }); };3.5 routes/user.js// routes/user.js const express require(express); const router express.Router(); const User require(../models/user); const auth require(../middlewares/auth); router.get(/, async (req, res, next) { try { const users await User.find().lean(); res.json({ code: 0, data: users }); } catch (err) { next(err); } }); router.post(/, auth, async (req, res, next) { try { const user await User.create(req.body); res.status(201).json({ code: 0, data: user }); } catch (err) { next(err); } }); module.exports router;3.6 app.js 主入口// app.js const express require(express); const mongoose require(mongoose); const morgan require(morgan); const config require(./config); const userRouter require(./routes/user); const errorHandler require(./middlewares/errorHandler); const app express(); // 1. 基础中间件 app.use(morgan(dev)); app.use(express.json()); // 2. 业务路由 app.use(/api/users, userRouter); // 3. 404 兜底 app.use((req, res, next) { const err new Error(接口不存在); err.status 404; next(err); }); // 4. 错误处理必须放最后 app.use(errorHandler); mongoose.connect(config.mongoUri) .then(() { app.listen(config.port, () { console.log(server running at http://127.0.0.1:${config.port}); }); }) .catch((err) { console.error(MongoDB 连接失败:, err.message); process.exit(1); });4. 中间件注册顺序与 curl 验证请求链路4.1 注册顺序为什么重要Express 中间件是顺序执行的注册顺序直接决定请求经过哪些处理。上面app.js的顺序是日志 → JSON 解析 → 路由 → 404 → 错误处理。如果把错误处理放到路由前面路由里next(err)抛出的错误就没人接。如果把express.json()放到路由后面req.body永远是空对象。这是新手最容易踩的坑。注意错误处理中间件必须注册在所有路由之后且必须写满四个参数否则 Express 会把它当普通中间件。4.2 用 curl 验证正常链路先启动 MongoDB再启动服务npm run dev另开一个终端先测 GET 请求curl -X GET http://127.0.0.1:3000/api/users预期返回{code:0,data:[]}再测 POST 创建用户注意带上x-auth-token头否则会被auth中间件拦截curl -X POST http://127.0.0.1:3000/api/users \ -H Content-Type: application/json \ -H x-auth-token: demo-token \ -d {name:alice,email:aliceexample.com}预期返回 201 和创建后的用户对象。再查一次 GET能看到刚创建的数据。4.3 验证错误处理链路故意不带 token 发 POSTcurl -X POST http://127.0.0.1:3000/api/users \ -H Content-Type: application/json \ -d {name:bob,email:bobexample.com}预期返回 401 和缺少认证 token。这说明auth中间件通过next(err)把错误交给了errorHandler。再访问一个不存在的路径curl -X GET http://127.0.0.1:3000/api/not-exist预期返回 404 和接口不存在。这说明 404 兜底中间件生效了。整条链路——日志、解析、路由、认证、错误处理——都能通过 curl 观察到这就是可复用骨架的价值每个环节职责单一出问题能快速定位。5. 本篇常见错排查5.1 req.body 为 undefined最常见的原因是express.json()没注册或者注册在路由之后。检查app.js里app.use(express.json())是否在app.use(/api/users, userRouter)之前。另外 curl 请求必须带Content-Type: application/json否则解析中间件不会处理。5.2 Mongoose 报 E11000 duplicate key这是唯一索引冲突。userSchema里name设了unique: true重复插入同名用户就会报这个错。错误会被next(err)捕获最终返回 500。如果想返回更友好的提示可以在路由里判断err.code 11000改成 409 状态码。5.3 错误处理中间件不生效九成是参数写成了三个。Express 靠函数参数个数判断是不是错误处理器必须写成(err, req, res, next)。另外确认它注册在所有路由之后否则路由抛错时它还没进流水线。5.4 中间件顺序导致 404 提前返回如果 404 兜底写在了路由前面所有请求都会先撞上 404。记住顺序基础中间件 → 业务路由 → 404 → 错误处理。这个顺序在骨架里固定下来新增路由时只往业务路由区加不动其他部分。5.5 MongoDB 连接失败先确认本地 MongoDB 是否启动再检查.env里的MONGO_URI端口和库名。如果用了远程库确认网络可达。连接失败时app.js里process.exit(1)会直接退出日志里能看到具体错误信息。6. 把骨架用起来下一步怎么扩展这套骨架跑通后新增业务模块的流程就固定了在models加 Schema在routes加路由在app.js业务路由区注册一行。中间件按需往middlewares加错误处理统一走errorHandler。npm 依赖按运行时和开发时分开管理配置全部走config和.env。如果你在调试接口时需要快速验证模型返回可以用模型对话入口直接试长期做编码或 Agent 相关开发Coding Plan 里有更完整的接入方式Key 的管理和生成在 API Keys 页面接入细节参考接入文档。骨架本身不依赖这些服务但把配置项预留出来后面接什么都方便。最后留一个实用习惯每次改完中间件顺序用 curl 把正常请求、认证失败、404 三条链路各跑一遍。三条都符合预期说明流水线没被改坏。这个动作花不了一分钟但能省掉大量排查时间。
企业数字化 ERP 产品动态
相关推荐
Manus 真的那么神?普通人用 TaoToken 也能搭一个 AI 代理 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 22:09:11
2026中企涌向印尼:商标注册、公司注册与法律顾问怎么选 【内容摘要】中企正加速布局印尼市场,商标抢注防护、商标续展、商标注册、公司注册与本地法律顾问,已成为出海合规的刚需环节。2025年中印尼贸易额达到1674.9亿美元,同比增长13.4%,投资与营商热度持续走高。本文按出海三阶段梳理合… · 2026/9/27 22:08:59
Blue---二分算法(二分查找与二分答案) 牛可乐和魔法封印 当区间具有二段性的时候就用二分来做。本题的意思就是找到数组中值为[x, y]区间的长度。由于是递增序列,那么必然存在二段性,直接用二分去做。唯一要注意的就是数组里可能不存在x或者y,二分查找出来的必然是>x的最小元素… · 2026/9/27 22:08:59
using-lwc - code-graph LWC CodeGraph 索引
使用时机
对已检出代码的结构性问题使用 CodeGraph:符号定义、签名、调用者/被调用者、依赖流、文件拓扑、可达性,或跨符号/文件的变更影响。
跳过时机
对于单文件字面编辑、仅格式化工作、仅文档/配置工作、注释/日志字符串… · 2026/9/27 23:57:03
Github周刊2026W37:用更少认知负荷换取更高产出效率的五个开发实践 1. 这期周刊到底在聊什么先说清楚,这不是一篇翻译稿,也不是简单的链接罗列。Github周刊2026W37这一期,我翻来覆去看了三遍,最大的感受是:它把当下开发者圈子里几个看似不搭界的热点,用一条暗线串起来了——… · 2026/9/27 23:56:57
Tencent BrowserSkill:已登录浏览器与编码Agent的本地桥接方案 1. 这个项目到底在解决什么问题先说结论:Tencent BrowserSkill 做的事情,用一句话概括就是——在“已经登录了各种账号的真实浏览器”和“跑在终端里的编码 Agent”之间,架一座本地桥。让 Agent 不用重新登录、不用重新配置 Cookie、不用去啃… · 2026/9/27 23:56:57
using-lwc - strong-context LWC 强上下文与标签
使用时机
对一小部分经过显式审查的核心页面——规则、操作手册、安全策略或运行手册——使用标签,这些页面必须在无相关性搜索的情况下完整加载。
跳过时机
不要将标签用作搜索别名、主题标签、推断关键词或加载广泛语料的方式。如果页面只是松… · 2026/9/27 23:56:57
OpenCV车牌识别实战:从定位到识别的完整Pipeline 简介:本资源是一套完整的Python毕业设计项目——基于OpenCV的车牌识别系统实现方案,面向计算机、人工智能及相关专业本科生,解决课程设计、毕设选题与图像处理实践中的核心需求。压缩包共2000个文件,含1987张实拍车牌JPG样本&… · 2026/9/27 23:56:51
基于SringBoot的智慧博物馆预约平台的设计与实现(源码+文档+部署讲解等) 联系博主 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 … · 2026/9/27 23:56:45
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01