使用TypeScript的Express服务器导入本地模块时出错
我现在陷入了死循环,解决一个问题就会触发另一个问题。核心问题是本地文件的导出与导入功能完全失效,运行时抛出如下错误:
node:internal/process/esm_loader:40 internalBinding('errors').triggerUncaughtException( ^ Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'C:\Users\[FULL PATH]\project\database' imported from C:\Users\[FULL PATH]\project\server.js Did you mean to import ../database.js? at new NodeError (node:internal/errors:406:5) at finalizeResolution (node:internal/modules/esm/resolve:233:11) at moduleResolve (node:internal/modules/esm/resolve:845:10) at defaultResolve (node:internal/modules/esm/resolve:1043:11) at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:383:12) at ModuleLoader.resolve (node:internal/modules/esm/loader:352:25) at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:228:38) at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:85:39) at link (node:internal/modules/esm/module_job:84:36) { url: 'file:///C:/Users/[FULL PATH]/project/database', code: 'ERR_MODULE_NOT_FOUND' }
我试过用module.exports = db2导出,导入时分别试过import * as db2 from './database'和const db2 = require('./database'),但每种方式都会出现对应的错误。
注意:这不是MySQL相关问题,测试其他本地模块也有同样问题,和async/await也无关。
运行环境:Node.js v20.9.0
相关代码文件
server.ts
import express from 'express' import dotenv from 'dotenv' /* EXPRESS */ const app = express() /* DOTENV */ dotenv.config() /* MYSQL */ //////////// THIS WORKS //////////// import mysql from 'mysql2/promise' const db1 = await mysql.createConnection({ host: process.env.MYSQL_HOST, user: process.env.MYSQL_USER, password: process.env.MYSQL_PASSWORD, database: process.env.MYSQL_DATABASE, }) console.log({db1: await db1.query('SELECT * FROM table')}) //////////// THIS DOES NOT WORK //////////// import db2 from './database' console.log({db2: db2.query('SELECT * FROM table')}) // BREAKS ON THIS LINE /* ROUTES */ app.get("/", (req, res) => { res.send("Hello, world!") }) /* START SERVER */ const PORT = process.env.PORT app.listen(PORT, () => { console.log(`App listening on port ${PORT}`) })
database.ts
import mysql from 'mysql2/promise' const db2 = await mysql.createConnection({ host: process.env.MYSQL_HOST, user: process.env.MYSQL_USER, password: process.env.MYSQL_PASSWORD, database: process.env.MYSQL_DATABASE, }) export default db2
package.json
{ "main": "server.js", "scripts": { "build": "tsc -p . -w", "start": "nodemon server.js" }, "type": "module", "dependencies": { "@types/express": "^4.17.21", "dotenv": "^16.3.1", "express": "^4.18.2", "mysql2": "^3.6.3" } }
tsconfig.json
{ "compilerOptions": { "target": "es2022", "module": "ES2022", "moduleResolution": "node", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true } }
解决方案
1. 修复导入路径的扩展名问题
Node.js ES模块要求本地模块导入必须指定完整文件扩展名。当前server.ts中导入./database,编译为server.js后,Node会尝试寻找无扩展名的database文件,但实际编译后的文件是database.js,因此修改导入语句为:
import db2 from './database.js'
如果希望在TS中直接使用.ts扩展名,可以在tsconfig.json的compilerOptions中添加:
"allowImportingTsExtensions": true, "resolveJsonModule": true
此时导入路径可以写./database.ts,但编译后仍需确保扩展名正确,推荐直接使用.js扩展名更稳妥。
2. 统一模块语法,避免混用
由于package.json已设置"type": "module",全程使用ES模块的import/export语法,不要混用CommonJS的require/module.exports,避免模块系统冲突。
3. 优化顶层await的使用(可选)
虽然你的Node版本支持顶层await,但将数据库连接包装为异步函数可以提升代码灵活性,避免潜在的模块加载问题:
修改database.ts:
import mysql from 'mysql2/promise' export default async function getDbConnection() { return mysql.createConnection({ host: process.env.MYSQL_HOST, user: process.env.MYSQL_USER, password: process.env.MYSQL_PASSWORD, database: process.env.MYSQL_DATABASE, }) }
在server.ts中调用:
import getDbConnection from './database.js' const db2 = await getDbConnection() console.log({db2: await db2.query('SELECT * FROM table')})
4. 检查编译输出
确认tsc编译后,database.js与server.js处于同一目录。若tsconfig.json未设置outDir,默认会将编译产物生成在源文件同目录,无需额外配置。
内容的提问来源于stack exchange,提问作者ToMakPo

