不使用Mongoose时,Express中重复调用MongoDB连接是否合理?
问题:Express + MongoDB原生驱动避免重复连接的方法
我正在为学习项目搭建Express后端,出于学习目的选择不使用Mongoose。目前已在config目录的db.config.js中实现MongoDB连接逻辑,server.js中调用connectToDb函数并成功启动服务。但在集合控制器中,每次执行CRUD操作都需要重新调用connectToDb函数,我疑惑这种方式是否正确,是否存在无需重复调用连接即可执行CRUD的方法?
注:我知道当前控制器的使用方式不规范,很多逻辑应放在服务文件中,但这是出于学习阶段逐步完善的考虑。
Server.js
require("dotenv").config({ path: "./config.env" }); const express = require("express"); const connectToDb = require("./configs/db.config"); const cors = require("cors"); const path = require("path"); const habitcardRouter = require("./routes/habitcardRoutes"); const app = express(); const PORT = process.env.PORT || 5000; // Middleware app.use(cors()); app.use(express.json()); app.use("/", express.static(path.join(__dirname, "/public"))); //First routes app.use("/api/habitcards", habitcardRouter); let db; connectToDb() .then((res) => { app.listen(PORT, () => { console.log(`Server is running on port: ${PORT}`); }); }) .catch((err) => { console.log(err); }); module.exports = { db };
db.config.js
const { MongoClient } = require("mongodb"); const uri = process.env.ATLAS_URI; const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true, }); const connectToDb = async () => { try { const database = client.db("howwasmyday"); return database; } finally { await client.close(); } }; module.exports = connectToDb;
habitcardController.js
const connectToDb = require("../configs/db.config"); //Create habitcard const createHabitCard = async (req, res) => { const habitcard = req.body; try { let db = await connectToDb(); const collection = db.collection("habitcards"); collection.insertOne(habitcard, (err, result) => { res.json("It all worked"); }); } catch (err) { console.log(err); } }; module.exports = { createHabitCard, };
解决方案
你的当前做法不正确,因为每次调用connectToDb都会先获取数据库实例,然后在finally里立刻关闭客户端连接,这会导致后续CRUD操作失败(连接已关闭),而且重复建立/关闭连接会严重影响性能。
正确的做法是在应用启动时建立一次连接,复用这个连接实例,直到应用关闭时再断开,具体改造步骤如下:
1. 修改db.config.js,复用MongoClient连接
不要在connectToDb里关闭客户端,而是保持连接打开,同时缓存数据库实例,避免重复连接:
const { MongoClient } = require("mongodb"); const uri = process.env.ATLAS_URI; const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true, }); // 缓存数据库实例 let dbInstance; const connectToDb = async () => { // 如果已经有连接,直接返回实例 if (dbInstance) { return dbInstance; } try { await client.connect(); // 实际建立连接 const database = client.db("howwasmyday"); dbInstance = database; console.log("MongoDB连接成功"); return database; } catch (err) { console.error("MongoDB连接失败:", err); throw err; } }; // 应用关闭时断开连接 const closeDbConnection = async () => { await client.close(); console.log("MongoDB连接已关闭"); }; module.exports = { connectToDb, closeDbConnection };
2. 修改server.js,初始化连接并共享数据库实例
在启动服务器前完成数据库连接,将数据库实例挂载到Express app对象上,方便后续调用:
require("dotenv").config({ path: "./config.env" }); const express = require("express"); const { connectToDb, closeDbConnection } = require("./configs/db.config"); const cors = require("cors"); const path = require("path"); const habitcardRouter = require("./routes/habitcardRoutes"); const app = express(); const PORT = process.env.PORT || 5000; // Middleware app.use(cors()); app.use(express.json()); app.use("/", express.static(path.join(__dirname, "/public"))); // 初始化数据库连接,成功后启动服务器 connectToDb() .then((db) => { // 将数据库实例挂载到app对象,方便中间件/控制器调用 app.locals.db = db; app.listen(PORT, () => { console.log(`Server is running on port: ${PORT}`); }); }) .catch((err) => { console.log(err); process.exit(1); // 连接失败直接退出进程 }); // 监听应用退出信号,关闭数据库连接 process.on("SIGINT", async () => { await closeDbConnection(); process.exit(0); }); module.exports = app;
3. 修改控制器,直接复用已有的数据库实例
不需要再调用connectToDb,从app.locals获取已连接的数据库实例:
//Create habitcard const createHabitCard = async (req, res) => { const habitcard = req.body; try { // 从app.locals获取已连接的数据库实例 const db = req.app.locals.db; const collection = db.collection("habitcards"); // 建议用async/await替代回调,保持代码风格统一 const result = await collection.insertOne(habitcard); res.json({ message: "创建成功", insertedId: result.insertedId }); } catch (err) { console.log(err); res.status(500).json({ error: "创建失败" }); } }; module.exports = { createHabitCard, };
关键说明
- MongoDB原生驱动的
MongoClient本身会维护连接池,无需手动重复建立连接,连接池会自动管理连接的复用和销毁。 - 应用启动时建立一次连接,整个生命周期复用该连接,直到应用关闭时再断开,这是生产环境的标准做法。
- 控制器里使用
req.app.locals.db可以直接拿到已初始化的数据库实例,避免重复连接的开销。
内容的提问来源于stack exchange,提问作者tomtom500
相关产品推荐
相关产品推荐

