恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
zeit/micro 接入 Mongo:用 Mongoose 打通 Serverless 数据层
首页
资讯中心
/
zeit/micro 接入 Mongo:用 Mongoose 打通 Serverless 数据层
zeit/micro 接入 Mongo:用 Mongoose 打通 Serverless 数据层
发布时间:2026/10/8 6:31:22
1. zeit/micro 里 Mongoose 连 Mongo 为什么总在冷启动翻车如果你正在用 zeit/micro 写轻量接口又想让数据落到 MongoDB大概率会搜到「zeit micro mongoose mongo 连接复用」这类关键词。micro 本身极简一个module.exports (req, res) {}就能跑起来但它没有内置数据库生命周期管理。Serverless 环境下每次冷启动都会重新执行模块顶层代码如果你把mongoose.connect()直接写在文件顶部就会出现两个典型问题一是每次请求都新建连接连接数暴涨二是函数实例被回收后连接悬空下一次请求拿到的是失效句柄报MongooseError: Operation buffering timed out或者Cannot use a closed connection。我试过在本地micro-dev跑得好好的一部署到 Serverless 平台就间歇性 500日志里全是buffering timed out after 10000ms。根因不是 Mongoose 本身而是连接没有做「实例级复用 就绪等待」。micro 的请求处理函数是 async 的但模块加载阶段是同步的mongoose.connect()返回 Promise你不 await 它第一个请求进来时连接还没建立Mongoose 默认会缓冲操作缓冲超时就抛错。这篇要解决的就是这条链路用 Mongoose 在 zeit/micro 里建立可复用的 Mongo 连接处理冷启动、连接超时、读写验证。适合已经在写 micro 接口、准备接 MongoDB、或者被 Serverless 连接问题卡住的开发者。下面从连接封装开始一步步给出可复制的配置和验证请求。2. TaoToken 前置给 micro 项目准备模型调用与 Key 管理在写数据库层之前先把项目里可能用到的模型调用能力准备好。很多 micro 服务除了读写 Mongo还会调用大模型做日志分类、内容摘要。TaoToken 提供统一的 API 入口兼容常见模型调用格式适合放在 Serverless 函数里做轻量推理。你需要先拿到 API Key。打开 https://taotoken.net/api-keys 登录后创建一个 Key复制保存。这个 Key 就是后面所有请求的凭证不要写死在代码里放到环境变量。模型调用入口是 https://taotoken.net/api 它兼容 OpenAI 风格的/v1/chat/completions。在 micro 里你可以直接用node-fetch或内置https模块请求。如果你要做长期编码或 Agent 类任务可以看 Coding Planhttps://taotoken.net/coding-plan 它更适合持续性的代码生成场景。想先验证模型是否通用模型对话页面https://taotoken.net/chat 发一条消息看返回。接入文档在 https://taotoken.net/doc 里面有 Base URL、鉴权头、请求体格式的完整说明。控制台在 https://taotoken.net/console 可以查看用量和调用记录。Claude Code 相关接入参考 https://taotoken.net/claude-code 。这里的关键是micro 函数是无状态的Key 必须通过环境变量注入不能硬编码。本地开发用.env部署时在平台的环境变量面板里配置。下面第三节会给出完整的连接封装和配置片段。3. 可复制配置Mongoose 连接封装与 micro 路由先装依赖。micro 生态的核心包是micro、micro-dev、microrouter数据库用mongoose。npm install micro micro-dev microrouter mongoose --savepackage.json的 scripts 建议这样写本地开发用micro-dev带热重载生产用micro{ name: micro-mongo, version: 1.0.0, main: index.js, scripts: { dev: micro-dev -p 5670, start: micro -p 5500 }, dependencies: { micro: ^9.3.4, microrouter: ^3.1.3, mongoose: ^7.6.3 }, devDependencies: { micro-dev: ^3.0.0 } }连接封装单独放db.js。核心是「缓存连接 Promise」而不是缓存连接对象。这样并发请求进来时大家 await 同一个 Promise不会重复建连。// db.js const mongoose require(mongoose); const MONGODB_URI process.env.MONGODB_URI || mongodb://127.0.0.1:27017/micro_log; let cached global.__mongooseCache; if (!cached) { cached global.__mongooseCache { conn: null, promise: null }; } async function connectWithDB() { if (cached.conn) { return cached.conn; } if (!cached.promise) { const opts { serverSelectionTimeoutMS: 5000, socketTimeoutMS: 45000, maxPoolSize: 10, bufferCommands: false }; cached.promise mongoose.connect(MONGODB_URI, opts).then((m) m); } cached.conn await cached.promise; return cached.conn; } module.exports connectWithDB;注意bufferCommands: false这样连接没就绪时操作会立刻报错而不是缓冲 10 秒再超时排障更快。maxPoolSize控制单实例连接数Serverless 下别设太大。模型定义放model/log.js用mongoose.models.Log做存在性判断避免热重载时重复注册模型报OverwriteModelError// model/log.js const mongoose require(mongoose); const LogSchema new mongoose.Schema({ name: { type: String, required: Please fill name, trim: true }, time: { type: String }, type: { type: String }, status: { type: String }, tag: { type: String }, handler: { type: String, default: SCM }, description: { type: String, default: SystemLog } }, { timestamps: true }); module.exports mongoose.models.Log || mongoose.model(Log, LogSchema);micro 入口index.js用 microrouter 组织路由每个 handler 先 await 连接再操作// index.js const { send, json } require(micro); const { router, get, post } require(microrouter); const connectWithDB require(./db); const Log require(./model/log); const notfound (req, res) send(res, 404, { msg: Not found route }); const create async (req, res) { await connectWithDB(); const data await json(req); const log new Log(data); await log.save(); send(res, 200, { msg: create-ok, id: log._id }); }; const findDatas async (req, res) { await connectWithDB(); const datas await Log.find({}).limit(50).lean(); send(res, 200, datas); }; const findDatasByTime async (req, res) { await connectWithDB(); const data await json(req); const p { time: { $gte: data.startTime, $lt: data.endTime } }; const datas await Log.find(p).lean(); send(res, 200, datas); }; module.exports router( get(/loglist, findDatas), post(/logcreate, create), post(/loglist, findDatasByTime), get(/*, notfound) );环境变量在本地用.env配合micro-dev加载或者直接export MONGODB_URI...。部署时在平台环境变量里配置MONGODB_URI格式是mongodbsrv://user:passhost/dbname或标准连接串。4. 验证请求本地与部署环境的读写链路确认先本地起服务npm run dev默认监听 5670。用 curl 验证写入curl -X POST http://localhost:5670/logcreate \ -H Content-Type: application/json \ -d {name:alarm-001,time:2024-06-01T10:00:00Z,type:cpu,status:open,tag:prod}预期返回{msg:create-ok,id:665f1a2b3c4d5e6f7a8b9c0d}再验证读取curl http://localhost:5670/loglist预期返回一个数组包含刚写入的记录。如果返回[]说明写入到了不同数据库或集合检查MONGODB_URI里的 dbname。按时间范围查询curl -X POST http://localhost:5670/loglist \ -H Content-Type: application/json \ -d {startTime:2024-06-01T00:00:00Z,endTime:2024-06-02T00:00:00Z}部署后把域名替换掉本地地址重复上面三条请求。Serverless 平台第一次请求会触发冷启动可能慢 1-2 秒第二次开始走复用连接响应明显变快。你可以连续发 10 次/loglist观察日志里connectWithDB是否只打印一次连接信息。如果每次都打印说明缓存没生效检查global.__mongooseCache是否被平台重置或者模块是否被重复加载。验证模型调用链路时用环境变量里的 Key 请求curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d {model:gpt-4o-mini,messages:[{role:user,content:ping}]}返回里有choices[0].message.content就说明 Key 和网络都通。这一步和 Mongo 无关但能确认你的 micro 函数在部署环境里能正常发出外部请求。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth报错一MongooseError: Operation buffering timed out after 10000ms这是连接没就绪就执行查询。检查两点connectWithDB()是否在每个 handler 里 await 了bufferCommands是否设成了false。如果设了 false 还报这个错说明连接根本没建立看MONGODB_URI是否正确、Mongo 是否允许当前 IP 访问。报错二MongoServerSelectionError: connect ECONNREFUSED 127.0.0.1:27017本地没起 Mongo或者连接串指向了 localhost 但部署环境里没有本地 Mongo。部署时必须用云数据库连接串通过环境变量注入。报错三OverwriteModelError: Cannot overwrite Log model once compiled.热重载时重复注册模型。用mongoose.models.Log || mongoose.model(Log, LogSchema)解决已经在第 3 节代码里处理了。报错四模型调用返回 401{error:{message:Invalid API key,type:invalid_request_error}}。检查Authorization头是否是Bearer KeyKey 是否有多余空格环境变量是否真的注入到了运行环境。在 TaoToken 控制台 https://taotoken.net/console 确认 Key 状态。报错五local proxy failed或连接超时这类错误通常出现在函数运行环境无法直连外部服务时。检查平台是否限制了出站网络或者连接串里的 host 是否可达。TaoToken 的 API 入口是标准 HTTPS正常环境可直接请求。报错六Cannot read properties of undefined (reading choices)模型返回体结构和你解析的字段不匹配。先打印完整响应体确认choices存在。常见原因是请求被拦截返回了 HTML 错误页或者用了错误的 endpoint。确认请求的是https://taotoken.net/api/v1/chat/completions。报错七OAuth 相关错误如果你在接入 Claude Code 或类似工具时遇到 OAuth 报错检查回调地址和 token 是否过期。Claude Code 接入参考 https://taotoken.net/claude-code 按文档里的步骤重新授权。排查顺序建议先确认 Mongo 连接串和网络再确认 Mongoose 缓存逻辑最后确认模型调用的 Key 和 endpoint。每一步都用最小请求验证不要一次改多个地方。6. 把连接复用和 Key 管理固定成项目模板micro Mongoose 这套组合的关键就三件事连接 Promise 缓存、bufferCommands: false、每个 handler await 连接。把db.js和model/log.js抽成模板新项目直接复制能省掉大量冷启动排障时间。模型调用这边Key 走环境变量Base URL 用 https://taotoken.net/api 需要长期跑编码任务就上 Coding Plan临时验证用模型对话页面。接入文档在 https://taotoken.net/doc API Key 在 https://taotoken.net/api-keys 创建。最后留一个实用技巧在connectWithDB里加一行连接事件监听mongoose.connection.on(error, console.error)部署后日志里能第一时间看到连接断开比等请求超时再查快得多。