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

基于子域名动态配置数据库URL的NestJS + Prisma中间件实现

基于子域名动态配置Prisma数据库连接(NestJS多租户场景)

核心思路

利用NestJS的请求级依赖注入,结合中间件提取的子域名,动态生成对应租户的数据库连接字符串,并缓存Prisma客户端实例以优化性能。

实现步骤

1. 缓存租户Prisma客户端实例

创建全局缓存容器,避免重复创建PrismaClient(每个实例对应一个数据库连接池,重复创建会消耗过多资源):

// src/prisma/prisma-client.cache.ts
import { PrismaClient } from '@prisma/client';

export const prismaClientCache = new Map<string, PrismaClient>();

2. 配置动态Prisma客户端提供者

在Prisma模块中,通过REQUEST对象获取子域名,生成对应数据库连接,并从缓存复用实例:

// src/prisma/prisma.module.ts
import { Module, Scope } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { PrismaClient } from '@prisma/client';
import { prismaClientCache } from './prisma-client.cache';

@Module({
  providers: [
    {
      provide: PrismaClient,
      scope: Scope.REQUEST, // 请求级别作用域,确保每个请求匹配对应租户的客户端
      useFactory: (req: any) => {
        const subdomain = req.subdomain;
        // 处理无子女域名的默认情况(如主域名连接默认数据库)
        if (!subdomain) {
          return new PrismaClient();
        }

        // 优先从缓存获取实例
        if (prismaClientCache.has(subdomain)) {
          return prismaClientCache.get(subdomain);
        }

        // 生成租户专属数据库URL(建议用环境变量模板替换,避免硬编码)
        const databaseUrl = `postgresql://${process.env.DB_USER}:${process.env.DB_PASS}@${process.env.DB_HOST}:${process.env.DB_PORT}/${subdomain}_db?schema=public`;
        
        const prisma = new PrismaClient({
          datasources: {
            db: { url: databaseUrl }
          },
          // 可选:自定义连接池配置
          // poolSize: 10
        });

        prismaClientCache.set(subdomain, prisma);
        return prisma;
      },
      inject: [REQUEST],
    },
  ],
  exports: [PrismaClient],
})
export class PrismaModule {}

3. 全局注册子域名提取中间件

确保中间件在所有请求前执行,将子域名挂载到请求对象:

// src/app.module.ts
import { Module, MiddlewareConsumer } from '@nestjs/common';
import { DynamicDbMiddleware } from './middlewares/dynamic-db.middleware';
import { PrismaModule } from './prisma/prisma.module';
// 导入你的业务模块
import { UserModule } from './user/user.module';

@Module({
  imports: [PrismaModule, UserModule],
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(DynamicDbMiddleware).forRoutes('*'); // 全局应用中间件
  }
}

4. 在业务服务中使用动态连接

直接注入PrismaClient即可,此时客户端已自动关联当前请求的租户数据库:

// src/user/user.service.ts
import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class UserService {
  constructor(private readonly prisma: PrismaClient) {}

  async getTenantUsers() {
    // 这里的prisma已连接当前子域名对应的数据库
    return this.prisma.user.findMany();
  }
}

注意事项

  • 环境变量优化:建议将数据库URL模板存入环境变量(如DATABASE_URL_TEMPLATE=postgresql://{user}:{pass}@{host}:{port}/{tenant}_db),通过字符串替换生成租户URL,避免硬编码敏感信息。
  • 错误处理:需捕获数据库连接失败的异常(如租户数据库不存在),返回友好的错误响应。
  • 缓存清理:如果租户可能被删除,需定期清理缓存中无效的PrismaClient实例,避免资源泄漏。

内容的提问来源于stack exchange,提问作者Abdulaziz mohammed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 19:17:38