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

Node.js中module.exports异步初始化问题:pool.query不是函数

PostgreSQL + Express + Node.js Todo应用的module.exports异步问题

我正在使用PostgreSQL、Express和Node.js构建一个简易Todo应用,遇到了module.exports相关的问题。

我的index.js代码如下:

const express = require("express");
 const cors = require("cors");
 const pool = require("./db");
 const app = express();
 const PORT = 5000;

 app.use(cors());
 app.use(express.json());
 console.log("2");
 app.get("/todos", async (req, res) => {
 try {
  const result = await pool.query("SELECT * FROM todo");
  res.json(result.rows);
} catch (err) {
  console.log(err);
}
});

我的db.js代码如下:

const fs = require("fs").promises;
const path = require("path");
const pgtools = require("pgtools");
const { Pool } = require("pg");
const dB = "perntodo";

const config = {
  user: "postgres",
  password: "my_pass",
  port: 5432,
  host: "localhost",
};

const createDB = async () => {
  try {
    await pgtools.createdb(config, dB);
    console.log(`database ${dB} created`);
  } catch (err) {
    console.log(err);
  }
};

createDB()
  .then(async () => {
    const pool = new Pool({
      ...config,
      database: dB,
    });

    const schemaPath = path.join(__dirname, "database.sql");
    const schema = await fs.readFile(schemaPath, "utf8");
    await pool.query(schema);
    return pool;
  })
  .then((pool) => {
    console.log("1");
    module.exports = pool;
  })
  .catch((err) => console.log(err));

database.sql仅包含简单的建表语句。

我遇到的问题是:module.exports在pool被new Pool实例化为对象前就执行了,导致index.js中出现错误:TypeError: pool.query is not a function。通过console.log发现,日志"2"先于"1"执行,说明Promise链存在问题。

我对db.js的控制流理解如下:首先调用createDB()函数创建perntodo数据库(不存在时),数据库创建完成后执行第一个.then()块,创建新的Pool并通过pool.query()将SQL schema文件加载到数据库,最后执行第二个.then()块将pool对象设为模块导出内容,确保应用其他部分能使用该对象。

想请教我的Promise链中到底存在什么错误?


问题根源

Node.js的模块加载是同步的:当index.js里执行require('./db')时,Node会立即同步执行db.js里的所有代码,但你的module.exports = pool是放在Promise的.then()回调里的——Promise回调是异步执行的,这意味着在模块加载完成时,module.exports还是默认的空对象,而非你期望的Pool实例。所以index.js拿到的pool是个空对象,自然没有query方法,也就抛出了错误。

日志"2"先于"1"执行,就是因为同步加载db.js时,Promise链还没走到.then()里的console.log("1"),而index.js里的console.log("2")是同步代码,会先执行。

解决方案

不能在异步回调里赋值module.exports,而是要导出一个Promise,让index.js等待这个Promise resolved后再拿到Pool实例。

修改db.js:

const fs = require("fs").promises;
const path = require("path");
const pgtools = require("pgtools");
const { Pool } = require("pg");
const dB = "perntodo";

const config = {
  user: "postgres",
  password: "my_pass",
  port: 5432,
  host: "localhost",
};

const createDB = async () => {
  try {
    await pgtools.createdb(config, dB);
    console.log(`database ${dB} created`);
  } catch (err) {
    // 如果数据库已存在,忽略错误(pgtools会抛出数据库已存在的错误)
    if (err.code !== "42P04") {
      console.log(err);
      throw err;
    }
  }
};

// 导出一个Promise,resolve后返回Pool实例
module.exports = (async () => {
  await createDB();
  
  const pool = new Pool({
    ...config,
    database: dB,
  });

  const schemaPath = path.join(__dirname, "database.sql");
  const schema = await fs.readFile(schemaPath, "utf8");
  await pool.query(schema);
  
  console.log("1");
  return pool;
})();

修改index.js:

需要等待db.js导出的Promise完成,才能拿到可用的Pool实例:

const express = require("express");
const cors = require("cors");
const app = express();
const PORT = 5000;

app.use(cors());
app.use(express.json());

// 等待Pool实例初始化完成
require("./db")
  .then((pool) => {
    console.log("2");
    app.get("/todos", async (req, res) => {
      try {
        const result = await pool.query("SELECT * FROM todo");
        res.json(result.rows);
      } catch (err) {
        console.log(err);
        res.status(500).json({ error: err.message });
      }
    });

    // 初始化完成后再启动服务器
    app.listen(PORT, () => {
      console.log(`Server running on port ${PORT}`);
    });
  })
  .catch((err) => {
    console.error("Failed to initialize database pool:", err);
    process.exit(1);
  });

关键说明:

  1. db.js里立即执行一个异步函数,返回Promise,这个Promise会在数据库创建、Pool初始化、Schema执行完成后resolve出Pool实例。
  2. index.js不再直接require得到pool,而是等待这个Promise resolve后再使用pool,同时确保服务器在数据库准备好之后再启动,避免请求进来时数据库还没初始化完成。
  3. 处理了数据库已存在的错误(pgtools创建已存在的数据库会抛出42P04错误),避免不必要的报错。

内容的提问来源于stack exchange,提问作者Shreyas Nair

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 19:02:40