NestJS如何根据请求参数动态连接不同MongoDB数据库
多租户动态MongoDB连接实现方案(NestJS)
核心思路
不要使用启动时静态加载的MongooseModule.forRoot配置,改为实现请求感知的动态连接管理服务,复用已创建的数据库连接避免重复建连开销,同时适配请求参数动态切换目标数据库。
1. 实现动态Mongoose连接服务
import { Injectable, Inject } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; import { Request } from 'express'; import * as mongoose from 'mongoose'; import { Connection } from 'mongoose'; import { ConfigService } from '@nestjs/config'; @Injectable() export class DynamicMongoService { // 缓存已创建的连接,key为数据库唯一标识(比如租户ID、自定义库标识) private connectionPool: Map<string, Connection> = new Map(); constructor( @Inject(REQUEST) private readonly request: Request, private readonly configService: ConfigService ) {} async getConnection(): Promise<Connection> { // 从请求中获取数据库标识参数,可根据业务调整为从query、body取 const dbKey = this.request.headers['x-db-identifier'] as string; if (!dbKey) { throw new Error('缺少目标数据库标识'); } // 已有有效连接直接返回 if (this.connectionPool.has(dbKey) && this.connectionPool.get(dbKey).readyState === 1) { return this.connectionPool.get(dbKey); } // 从配置中心/配置文件读取对应数据库的连接信息 const dbConfig = this.configService.get(`dbConfigs.${dbKey}`); if (!dbConfig) { throw new Error('目标数据库配置不存在'); } // 新建连接 const newConnection = await mongoose.createConnection( `mongodb://${dbConfig.user}:${dbConfig.pwd}@${dbConfig.host}:${dbConfig.port}/${dbKey}?authSource=admin` ).asPromise(); // 存入缓存 this.connectionPool.set(dbKey, newConnection); // 监听连接断开事件,清理无效缓存 newConnection.on('disconnected', () => { this.connectionPool.delete(dbKey); }); return newConnection; } // 封装模型获取方法,避免重复注册模型 async getModel<T extends mongoose.Document>(modelName: string, schema: mongoose.Schema): Promise<mongoose.Model<T>> { const conn = await this.getConnection(); if (conn.models[modelName]) { return conn.model<T>(modelName); } return conn.model<T>(modelName, schema); } }
2. 模块注册配置
不需要引入静态的MongooseModule.forRoot配置,直接在业务模块注册动态连接服务即可:
import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { DynamicMongoService } from './dynamic-mongo.service'; import { BusinessController } from './business.controller'; import { BusinessService } from './business.service'; @Module({ imports: [ConfigModule.forRoot()], providers: [DynamicMongoService, BusinessService], controllers: [BusinessController], exports: [DynamicMongoService] }) export class BusinessModule {}
3. 业务层使用示例
import { Injectable } from '@nestjs/common'; import { DynamicMongoService } from './dynamic-mongo.service'; import { UserSchema, UserDocument } from './schemas/user.schema'; @Injectable() export class BusinessService { constructor(private readonly dynamicMongoService: DynamicMongoService) {} async getUserList() { const UserModel = await this.dynamicMongoService.getModel<UserDocument>('User', UserSchema); return UserModel.find().exec(); } async addUser(userData: Record<string, any>) { const UserModel = await this.dynamicMongoService.getModel<UserDocument>('User', UserSchema); const newUser = new UserModel(userData); return newUser.save(); } }
问题说明
MongooseModule.forRoot是应用启动阶段就初始化的静态配置,本身设计就是固定连接场景使用,确实无法接收运行时的请求参数。- 直接调用
mongoose.createConnection报依赖错误,大概率是你没有将连接对应的提供者正确注册到Nest的DI容器,或者在单例服务中注入了请求级别的参数,上述方案直接将请求注入连接管理服务,不需要额外注册连接提供者,可规避依赖解析问题。
注意事项
- 可根据业务给连接池加闲置过期规则,避免无效连接占用过多资源
- 做好数据库标识参数的校验和权限控制,避免越权访问其他数据库
- 敏感连接信息不要硬编码,统一通过配置服务读取
内容的提问来源于stack exchange,提问作者Thông
相关产品推荐
相关产品推荐

