You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

不使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 13:05:25