PrismaModule:测试异步多数据库连接
本节通过实践验证 forRootAsync 的完整逻辑,创建 PrismaService 实现多租户场景下的异步多数据库连接,使同一个 ORM 能够访问不同类型的数据库(MySQL 和 PostgreSQL)。
创建 PrismaService
新建 prisma.service.ts 文件,实现 PrismaOptionsFactory 接口:
import { Request } from 'express';
import { PrismaOptionsFactory } from './prisma-core.module';
import { PrismaModuleOptions } from './interfaces';
export class PrismaService implements PrismaOptionsFactory {
constructor(@Inject(REQUEST) private request: Request) {}
async createPrismaOptions(): Promise<PrismaModuleOptions> {
// 从请求中获取租户标识
const { tenantId } = this.request.headers as any;
// 根据不同租户返回不同数据库连接配置
if (tenantId === 'default') {
return {
name: 'prisma-1',
datasourceUrl: 'mysql://root:123456@localhost:3306/testdb',
};
} else if (tenantId === 'default2') {
return {
name: 'prisma-2',
datasourceUrl: 'postgresql://postgres:123456@localhost:5432/testdb',
};
}
throw new Error(`Unknown tenant: ${tenantId}`);
}
}
typescript
在 AppModule 中使用 forRootAsync
将同步配置切换为异步配置:
// 修改前(同步)
// PrismaModule.forRoot({ ... })
// 修改后(异步)
PrismaModule.forRootAsync({
useClass: PrismaService,
})
typescript
这样数据库连接逻辑不再直接写在 Module 中,而是通过 PrismaService 根据请求上下文动态决定。
注入名称的匹配
使用 useClass 后,需要在 Controller 中注入时指定 Provider 名称:
@Controller()
export class AppController {
constructor(
@Inject('prisma-1') private prismaClient: PrismaClient,
) {}
@Get()
async findAll() {
return this.prismaClient.user.findMany();
}
}
typescript
关键点:
@Inject()中的名称必须与PrismaModuleOptions中设置的name字段一致。
测试流程与问题排查
测试步骤
- 启动调试进程:
pnpm start:dev - 使用接口请求工具发送请求,在 Header 中携带
tenantId - 分别测试
tenantId=default(MySQL)和tenantId=default2(PostgreSQL)
常见问题
问题一:Cannot destructure property url of PrismaModuleOptions
TypeError: Cannot destructure property 'datasourceUrl' of
prismaModuleOptions as it is undefined.
text
原因:请求中携带的 tenantId 值不在预定义的映射范围内。需确保 tenantId 与代码中的判断条件匹配。
问题二:数据库连接异常
Error: P1003: Database `testdb` does not exist
text
原因:目标数据库尚未创建。需要通过 Docker 连接到对应的数据库容器,手动创建数据库。
测试结果验证
# 请求 default 租户(MySQL)
curl -H "tenantId: default" http://localhost:3000/v1
# 返回 MySQL 数据库中的数据
# 请求 default2 租户(PostgreSQL)
curl -H "tenantId: default2" http://localhost:3000/v1
# 返回 PostgreSQL 数据库中的数据
bash
成功通过同一个 ORM(Prisma)根据不同的租户标识访问了不同类型的数据库。
架构优势
通过 forRootAsync + useClass 的方式实现多租户数据库连接,相比直接在 Module 中硬编码配置有以下优势:
- 配置逻辑与模块定义分离:数据库连接逻辑集中在 Service 中,Module 保持简洁
- 请求级别的动态配置:每次请求都可以根据上下文动态选择数据库
- 可扩展性:后续可以通过注入
ConfigService来从配置中心或数据库读取租户配置 - 可测试性:Service 可以独立进行单元测试
本节总结
- 创建
PrismaService实现PrismaOptionsFactory接口,根据tenantId返回不同数据库配置 - 在 AppModule 中使用
PrismaModule.forRootAsync({ useClass: PrismaService })替换同步配置 - 注入时必须使用
@Inject(name)匹配PrismaModuleOptions中设置的name - 成功验证了 MySQL 和 PostgreSQL 两种数据库的异步连接
↑