Node.js类中require加载异常:生产环境数据库包未定义
Hey there! Let's break down what's going on here and fix that frustrating ReferenceError you're seeing in production.
Your issue boils down to variable scope. Even though require is synchronous, you declared mssql and oracledb inside the if block that checks for the TEST environment variable. Variables declared with const or let are block-scoped—meaning they only exist inside that specific if block. When your production environment runs the get_pool() method, it tries to access mssql/oracle from the outer scope, where those variables don't exist, hence the error.
Take a look at your original code snippet to see the problem clearly:
if (typeof process.env.TEST === 'undefined' || process.env.TEST === null){ const mssql = require('mssql'); // This variable only exists inside this if block! const oracle = require('oracledb'); // ... password handling logic ... }
Then in the get_pool() method:
if (config.Logging.DB.type == 'mssql'){ if (!this._pool) { this._pool = new mssql.ConnectionPool(sqlDbOptions); // Trying to access outer-scope mssql, which doesn't exist } // ... rest of the code ... }
Here are a couple of clean, Node.js-friendly solutions to resolve this:
Solution 1: Hoist Variable Declarations to Module Scope
Move the mssql and oracle variable declarations outside the if block, then assign them inside the conditional. This makes them available across the entire module while still skipping loading in test environments:
"use strict"; const config = require('../../config/mainConfigs'); // ... Other constants ... // Declare variables at module scope so they're accessible everywhere let mssql, oracle; // Simplified environment check (easier to read and maintain!) const isTestEnv = process.env.TEST === 'true' || process.env.TEST === true; if (!isTestEnv) { mssql = require('mssql'); oracle = require('oracledb'); // ... Your password handling logic ... } class db { constructor(){ this._pool = null; } get_pool(){ if (isTestEnv) return Promise.resolve(); // Shorter syntax for a resolved promise if (config.Logging.DB.type === 'mssql'){ if (!this._pool) { this._pool = new mssql.ConnectionPool(sqlDbOptions); } return this._pool.connected ? Promise.resolve(this._pool) : this._pool.connect(); } else if (config.Logging.DB.type === 'oracle'){ if (!this._pool || this._pool.connectionsOpen === 0){ return oracle.getConnection(oracleDbOptions).then(pool => { this._pool = pool; return pool; }); } return Promise.resolve(this._pool); } } // ... MORE CLASS OPERATIONS ... } module.exports = db;
Solution 2: Dynamic Loading in get_pool()
If you prefer not to hoist variables, you can load the database modules directly inside the get_pool() method when you need them. Since require caches modules, this won't cause redundant loads, and you avoid polluting the module scope:
get_pool(){ if (process.env.TEST) return Promise.resolve(); if (config.Logging.DB.type === 'mssql'){ const mssql = require('mssql'); // Load only when this branch runs if (!this._pool) { this._pool = new mssql.ConnectionPool(sqlDbOptions); } return this._pool.connected ? Promise.resolve(this._pool) : this._pool.connect(); } else if (config.Logging.DB.type === 'oracle'){ const oracle = require('oracledb'); if (!this._pool || this._pool.connectionsOpen === 0){ return oracle.getConnection(oracleDbOptions).then(pool => { this._pool = pool; return pool; }); } return Promise.resolve(this._pool); } }
In JavaScript, const and let create block-scoped variables. So when you declared mssql inside that if block, it only existed within that block. When production code tried to use it in get_pool(), the variable wasn't defined in that scope. Moving require outside fixed it because the variable became module-scoped, but that broke your test environment—so we needed a way to keep the conditional load while making the variables accessible where needed.
内容的提问来源于stack exchange,提问作者Fede E.

