TypeScript单体仓库中跨应用共享Mongoose连接时模型无法加载的问题
Hey there! Let's dig into why your models aren't loading even though your MongoDB connection shows as ready (readyState: 1). Based on your monorepo setup and problem description, here are the most likely causes and actionable fixes:
1. 重复的Mongoose实例(最常见根源)
In a monorepo without workspace tools like Lerna/NX, if each app (app1, app2) and the common folder have their own node_modules with Mongoose installed, you end up with multiple independent Mongoose instances. This leads to:
- The
commoncode using one Mongoose instance (which is connected, hencereadyState:1). - Your app models being registered on a different Mongoose instance (from the app's own
node_modules) that isn't connected.
Fix:
Ensure all projects share a single Mongoose instance:
Option A: Use npm/yarn Workspaces
Add a root package.json to enable dependency hoisting:
{ "name": "my-backend-monorepo", "private": true, "workspaces": ["./app1", "./app2", "./common"] }
Then run npm install or yarn install from the root directory. This will install all dependencies (including Mongoose) in a single root node_modules folder, ensuring all projects use the same instance.
Option B: Force Mongoose Version Consistency
If you don't want to use workspaces, add an override to each app's package.json to force Mongoose to resolve to the same version:
// app1/package.json "overrides": { "mongoose": "^7.6.0" // Match the version in common/package.json }
Do the same for app2 and ensure common/package.json uses the exact same Mongoose version.
2. Models Bound to the Wrong Connection Instance
Mongoose models are tied to specific connection instances. If your common code uses a custom connection (instead of the default one), but your app models are registered on the default (unconnected) instance, queries will fail even if the common connection is ready.
Fix:
Case 1: Using Default Connection
If your common code uses mongoose.connect() (default connection), ensure your app models are registered on this same instance. Verify this by logging:
// In app1: import mongoose from 'mongoose'; console.log(XModel.db === mongoose.connection); // Should log true
Case 2: Using Custom Connection
If you switch to a custom connection in common, expose it and bind models to it:
// common/database/Database.ts import mongoose from 'mongoose'; export class Database { private static instance: Database; public connection: mongoose.Connection; // Expose the connection private isConnected: boolean = false; private constructor() {} public static async getInstance(): Promise<Database> { if (!Database.instance) { Database.instance = new Database(); await Database.instance.establishConnection(); } return Database.instance; } private async establishConnection(): Promise<void> { if (this.isConnected) return; try { if (process.env.NODE_ENV !== 'production') mongoose.set('debug', true); // Create and store custom connection this.connection = await mongoose.createConnection( dbConnectionConfig.url, dbConnectionConfig.options ); this.isConnected = true; } catch (err) { throw err; } } }
Then in your app, register models using this shared connection:
// app1/models/XModel.ts import { Database } from '@common/database'; import { XSchema } from './XSchema'; import { COLLECTION_NAME } from '../constants'; // Wait for the shared connection first const dbInstance = await Database.getInstance(); export const XModel = dbInstance.connection.model<IX>(COLLECTION_NAME.X, XSchema);
3. Fix Async Initialization Order
Your original App.getInstance() doesn't wait for the database connection to complete before initializing the app. This can lead to models being registered before the connection is ready (even though Mongoose queues operations, this can cause confusion with model registration).
Update your app's getInstance to be async:
// app1/app.ts public static async getInstance({ routes = [] }: { routes?: IRoute[] } = {}): Promise<App> { if (!App.instance) { try { // Wait for database connection to be fully ready await Database.getInstance(); App.instance = new App(routes); } catch (err) { logger.error('Database connection failed', err); process.exit(1); } } return App.instance; }
4. Verify TypeScript Path Configuration
Ensure your app's tsconfig.json correctly resolves imports from the common folder:
// app1/tsconfig.json { "compilerOptions": { "baseUrl": "./", "paths": { "@common/*": ["../common/*"] // Adjust path to match your folder structure } } }
This ensures TypeScript can find and import common code without module resolution errors.
Validation Checks
To confirm the issue:
- Check for duplicate Mongoose instances: Run
console.log(require.resolve('mongoose'))in bothapp1andcommon—if paths differ, you have duplicates. - Check model connection state: In your app, log
XModel.db.readyState—if it's 0 (disconnected), your model is on the wrong connection. - Check model registration: Log
mongoose.models[COLLECTION_NAME.X](or custom connection's models) to ensure the model exists.
These steps should help you resolve the model loading issue and get your shared database connection working across all apps.
备注:内容来源于stack exchange,提问作者Nick

