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

如何将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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 21:15:42