Node.js/Express:如何将Mongo数据库连接共享给其他模块?
解决Node.js中MongoDB连接共享与状态检测问题
当然可以共享单个MongoDB连接实例,MongoClient本身就设计为可复用的单例对象,驱动会自动维护连接池,直到你主动调用client.close()才会断开连接。你的代码问题出在异步函数中动态修改模块导出,导致其他模块导入时无法获取到已初始化的client。
问题根源
模块的module.exports是同步加载的,当其他模块导入你的数据库模块时,connect()异步函数还没执行完,此时导出的是默认的空对象,自然会出现undefined或is not a function错误。
正确的连接共享实现
方案1:用Promise封装连接过程(通用方案)
创建独立的db.js模块,通过Promise确保获取到的是已连接的client:
const { MongoClient } = require('mongodb'); const uri = '你的MongoDB连接字符串'; const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true, }); // 缓存连接Promise,避免重复发起连接请求 let connectPromise; // 获取已连接的client async function getClient() { if (!connectPromise) { connectPromise = client.connect() .then(() => { console.log('MongoDB连接成功'); return client; }) .catch(err => { console.error('MongoDB连接失败:', err); // 连接失败后重置Promise,允许后续重试 connectPromise = null; throw err; }); } return connectPromise; } // 检测连接是否处于打开状态 function isConnected() { // 检查topology是否存在且已连接 return client.topology && client.topology.isConnected(); } module.exports = { getClient, isConnected };
其他模块使用方式
const { getClient, isConnected } = require('./db'); async function insertProductionLog(doc) { // 可选:提前检测连接状态 if (!isConnected()) { console.log('连接未就绪,正在初始化连接...'); } // 确保获取到已连接的client const client = await getClient(); await client.db('syslogs').collection('production').insertOne(doc); }
方案2:Express项目中结合app.locals
如果你用Express,可以在服务器启动前完成连接,再将client挂载到app.locals:
// app.js const express = require('express'); const { getClient } = require('./db'); const app = express(); async function startServer() { // 先完成MongoDB连接 const mongoClient = await getClient(); // 将client挂载到app.locals,供路由使用 app.locals.mongoClient = mongoClient; // 配置中间件和路由 app.use(express.json()); app.post('/logs/production', async (req, res) => { try { await req.app.locals.mongoClient.db('syslogs').collection('production').insertOne(req.body); res.status(200).send('日志插入成功'); } catch (err) { res.status(500).send(`插入失败: ${err.message}`); } }); app.listen(3000, () => { console.log('服务器运行在http://localhost:3000'); }); } // 启动服务器 startServer().catch(err => console.error('服务器启动失败:', err));
关键注意事项
- 始终复用同一个MongoClient实例,不要每次操作都创建新实例,否则会耗尽连接池资源
- 不要在异步函数中修改
module.exports,模块导出是同步机制,异步修改无法被其他模块捕获 - 连接状态检测依赖
client.topology.isConnected(),这是MongoDB Node驱动提供的官方方法 - 应用关闭时,记得调用
client.close()释放连接:
// 捕获进程退出信号,关闭连接 process.on('SIGINT', async () => { await client.close(); console.log('MongoDB连接已关闭'); process.exit(0); });
内容的提问来源于stack exchange,提问作者Ronnie Smith
相关产品推荐
相关产品推荐

