Fastify注册Mongoose连接函数触发插件启动超时错误的问题排查
问题根源
你遇到的插件超时报错,是Fastify底层插件加载机制和你代码的写法冲突叠加导致的,核心有三个问题:
- Fastify(底层依赖avvio做插件加载)对插件初始化完成的判定是两套完全互斥的机制,混用就会导致框架无法感知插件状态:
- Promise模式:插件必须是纯async函数,且形参数量<=2(不声明
done参数),框架会等待函数返回的Promise状态变化,判定初始化完成 - 回调模式:插件是普通同步函数,形参数量为3(依次接收fastify实例、配置项、done回调三个参数),框架会等待插件内部主动调用
done(err)后,判定初始化完成 - 你写的代码是async函数,同时在形参里声明了第三个
done参数,触发了avvio的兼容逻辑:只要函数形参数为3,不管是不是async,框架都会进入回调等待模式,只跟踪done的调用时机,不会主动等待async函数内部的Promise状态。 - 因为async函数的特性,你的函数被调用后会立刻执行到第一个
await mongoose.connect()就挂起暂停,如果MongoDB连接耗时超过avvio默认的10秒超时阈值,框架就会先抛出超时错误,根本等不到后续代码里done()的调用。你之前把done包在setTimeout()里的操作没有解决本质问题,只是把done的调用时机进一步延后,自然也消不掉报错。
另外你代码里还有两个隐患:
- 连接MongoDB出错时,你只在catch块里打印日志,没有把错误抛出或者传递给done,会出现连接失败但应用强行启动的问题
- 连接参数里的
useNewUrlParser、useUnifiedTopology、useCreateIndex属于Mongoose旧版本的废弃配置,现在新版本已经不再需要传入,额外传入还会触发警告
正确实现
按照Fastify的插件规范,二选一实现即可,不要混用两种模式:
方案1:纯async/await风格(推荐)
完全去掉done参数,靠async函数的返回值让框架自动判定初始化状态,配合fastify-plugin避免全局挂载的实例被Fastify作用域隔离:
import fp from 'fastify-plugin' import mongoose from 'mongoose' import type { FastifyInstance } from 'fastify' export default fp(async (fastify: FastifyInstance, options: { uri: string }) => { try { await mongoose.connect(options.uri) // 把mongoose实例挂载到全局,后续路由可直接通过fastify.mongoose访问 fastify.decorate('mongoose', mongoose) fastify.log.info('MongoDB connection successful.') } catch (err) { fastify.log.error(`MongoDB connection failed: ${(err as Error).message}`) // 抛出错误,让框架终止启动流程 throw err } })
方案2:回调风格
用普通同步函数,靠手动调用done通知框架初始化状态:
import fp from 'fastify-plugin' import mongoose from 'mongoose' import type { FastifyInstance, DoneFuncWithErrOrRes } from 'fastify' export default fp(function (fastify: FastifyInstance, options: { uri: string }, done: DoneFuncWithErrOrRes) { mongoose.connect(options.uri) .then(() => { fastify.decorate('mongoose', mongoose) fastify.log.info('MongoDB connection successful.') done() }) .catch((err) => { fastify.log.error(`MongoDB connection failed: ${err.message}`) done(err) }) })
注册方式
实现完成后直接通过fastify.register()注册即可,不需要额外调整逻辑:
fastify.register(mongoosePlugin, { uri: '你的MongoDB连接地址' })
内容的提问来源于stack exchange,提问作者jstarnate
相关产品推荐
相关产品推荐

