如何将TypeORM Datasource从中间件注入到NestJS中
实现请求级TypeORM Datasource的NestJS注入
当然可以通过NestJS的依赖注入实现,核心思路是利用请求作用域(Request Scope)的Provider,结合中间件将动态创建的Datasource绑定到当前请求上下文,然后通过DI注入到控制器或服务中。
步骤1:定义请求级Datasource的注入Token
先创建一个唯一的符号作为注入标识,避免和全局Datasource冲突:
export const REQUEST_DATASOURCE = Symbol('REQUEST_DATASOURCE');
步骤2:注册请求作用域的Provider
在模块中注册一个请求作用域的Provider,它会从当前请求对象中获取已创建的Datasource:
import { Module, Scope, Inject, REQUEST } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { REQUEST_DATASOURCE } from './constants'; @Module({ providers: [ { provide: REQUEST_DATASOURCE, useFactory: (req: Request) => { // 对应中间件挂载到req上的Datasource属性 return req['datasource'] as DataSource; }, inject: [REQUEST], scope: Scope.REQUEST, // 标记为请求作用域,确保每个请求都有独立实例 }, ], exports: [REQUEST_DATASOURCE], // 导出供其他模块使用 }) export class DatasourceModule {}
步骤3:实现动态创建Datasource的中间件
在中间件中根据请求上下文(比如请求头、参数)创建Datasource,挂载到请求对象上,并在请求结束后销毁连接:
import { Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; import { DataSource } from 'typeorm'; @Injectable() export class DatasourceMiddleware implements NestMiddleware { async use(req: Request, res: Response, next: NextFunction) { // 从请求中获取数据库配置(示例:从请求头取目标数据库名称) const dbName = req.headers['x-target-db'] as string; // 动态创建TypeORM DataSource const datasource = new DataSource({ type: 'mysql', // 根据你的数据库类型调整 host: 'your-db-host', port: 3306, username: 'your-db-username', password: 'your-db-password', database: dbName, entities: [/* 你的实体类列表 */], synchronize: false, // 生产环境建议关闭 }); // 初始化数据库连接 await datasource.initialize(); // 将Datasource挂载到请求对象 req['datasource'] = datasource; // 请求结束后自动销毁连接,避免泄漏 res.on('finish', async () => { await datasource.destroy(); }); next(); } }
别忘了在模块中配置中间件:
@Module({ imports: [DatasourceModule], }) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer.apply(DatasourceMiddleware).forRoutes('*'); // 对所有路由生效,或指定特定路由 } }
步骤4:在控制器/服务中注入使用
通过自定义Token或装饰器注入请求级的Datasource:
import { Controller, Get, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { REQUEST_DATASOURCE } from './constants'; import { ExampleEntity } from './entities/example.entity'; @Controller('examples') export class ExamplesController { constructor( @Inject(REQUEST_DATASOURCE) private readonly datasource: DataSource, ) {} @Get() async getExamples() { const exampleRepo = this.datasource.getRepository(ExampleEntity); return exampleRepo.find(); } }
如果想让注入写法更优雅,可以自定义装饰器:
import { Inject } from '@nestjs/common'; import { REQUEST_DATASOURCE } from './constants'; export const InjectRequestDatasource = () => Inject(REQUEST_DATASOURCE);
然后控制器中就可以这样用:
constructor( @InjectRequestDatasource() private readonly datasource: DataSource, ) {}
注意事项
- 请求作用域的Provider会导致依赖它的控制器、服务也变成请求作用域,每个请求都会创建新的实例,需要评估性能影响(动态数据库连接场景下这是合理的)。
- 务必在请求结束后销毁Datasource,避免数据库连接泄漏。
- 如果需要在多个模块中使用,只需导入
DatasourceModule即可。
内容的提问来源于stack exchange,提问作者Sam
相关产品推荐
相关产品推荐

