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

基于Typegoose与NestJS框架实现多租户:动态创建独立数据库连接的可行性咨询

实现Typegoose + NestJS的动态多租户数据库连接

当然可以实现!我之前在NestJS + Typegoose的项目里做过类似的多租户独立数据库方案,核心思路是动态创建Mongoose连接,再将这些连接绑定给Typegoose模型,从而让不同租户的模型操作各自的专属数据库。

核心原理

Typegoose本质是Mongoose的封装,所以动态连接的关键在于管理Mongoose的Connection实例,然后在创建Typegoose模型时指定对应的existingConnection参数,替代默认的全局连接。

具体实现步骤

1. 构建租户数据库连接管理服务

首先创建一个全局服务,用来维护所有租户的数据库连接池,避免重复创建连接:

import { Injectable, OnModuleDestroy } from '@nestjs/common';
import { createConnection, Connection } from 'mongoose';
import { getModelForClass, ClassType } from '@typegoose/typegoose';

@Injectable()
export class TenantDbService implements OnModuleDestroy {
  // 用Map存储租户ID与对应连接的映射
  private tenantConnections: Map<string, Connection> = new Map();

  /**
   * 获取或创建租户专属连接
   * @param tenantId 租户唯一标识
   * @param dbConfig 租户数据库配置(URI、用户名、密码等)
   */
  async getTenantConnection(tenantId: string, dbConfig: { uri: string }): Promise<Connection> {
    // 连接已存在则直接返回
    if (this.tenantConnections.has(tenantId)) {
      return this.tenantConnections.get(tenantId)!;
    }

    // 创建新连接
    const connection = await createConnection(dbConfig.uri, {
      // 可根据需求添加Mongoose连接选项
      autoIndex: false,
    }).asPromise();

    this.tenantConnections.set(tenantId, connection);
    return connection;
  }

  /**
   * 获取租户专属模型
   * @param tenantId 租户唯一标识
   * @param dbConfig 租户数据库配置
   * @param modelClass Typegoose模型类
   */
  async getTenantModel<T>(
    tenantId: string,
    dbConfig: { uri: string },
    modelClass: ClassType<T>,
  ) {
    const connection = await this.getTenantConnection(tenantId, dbConfig);
    // 指定existingConnection绑定到租户连接
    return getModelForClass(modelClass, { existingConnection: connection });
  }

  // 模块销毁时关闭所有租户连接,避免资源泄漏
  async onModuleDestroy() {
    await Promise.all(
      Array.from(this.tenantConnections.values()).map((conn) => conn.close()),
    );
  }
}

2. 定义Typegoose模型类

和普通模型定义一样,不需要额外修改,只需要确保模型类是可复用的:

import { modelOptions, Prop } from '@typegoose/typegoose';

@modelOptions({ schemaOptions: { collection: 'products' } })
export class Product {
  @Prop({ required: true })
  name: string;

  @Prop({ required: true, type: Number })
  price: number;
}

3. 在控制器/服务中动态使用租户模型

在业务逻辑中,根据当前租户ID获取对应的模型,再执行数据库操作:

import { Controller, Get, Param } from '@nestjs/common';
import { TenantDbService } from './tenant-db.service';
import { Product } from './product.model';

// 假设从URL参数获取租户ID
@Controller('tenants/:tenantId/products')
export class TenantProductController {
  constructor(private readonly tenantDbService: TenantDbService) {}

  @Get()
  async getProducts(@Param('tenantId') tenantId: string) {
    // 这里建议从主数据库/配置中心获取租户的数据库配置,而非硬编码
    const tenantDbConfig = {
      uri: `mongodb://localhost:27017/tenant_${tenantId}`,
    };

    // 获取当前租户的Product模型
    const ProductModel = await this.tenantDbService.getTenantModel(
      tenantId,
      tenantDbConfig,
      Product,
    );

    // 执行常规的数据库操作
    return ProductModel.find().exec();
  }
}

关键注意事项

  • 连接池优化:定期清理闲置的租户连接,或者在租户删除时主动关闭对应连接,避免占用过多数据库连接资源。
  • 配置安全:租户的数据库凭证(用户名、密码)不要硬编码,建议存储在加密的配置中心或主数据库中,使用时动态获取。
  • 事务支持:由于每个租户是独立数据库,跨租户事务无法实现,只能在单个租户的连接内处理事务。
  • 全局模块注册:将TenantDbService注册为全局模块,方便在整个应用的控制器和服务中注入使用。

内容的提问来源于stack exchange,提问作者GAURAV RAJPUT

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 20:07:43