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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 23:36:05