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

执行node dist/openapi.js生成OpenAPI时NestJS报Connection未找到错误

问题:NestJS生成OpenAPI文档时TypeOrm连接在shutdown阶段报错

NestJS项目在dev/prod模式启动正常,但执行node dist/openapi.js更新openapi.yaml时,抛出Nest无法找到Connection元素的错误,错误栈指向TypeOrmCoreModule的onApplicationShutdown阶段。

项目配置了两个数据库连接:无名称的Oracle连接(GOLD)、名为'reflex'的PostgreSQL连接;ReflexRepository通过@InjectConnection('reflex')注入连接,OmsModule将ReflexRepository声明为provider。


错误信息

2023-06-26T09:45:01+02:00 - OmsModule dependencies initialized
(node:10136) UnhandledPromiseRejectionWarning: Error: Nest could not find Connection element (this provider does not exist in the current context)
    at ContainerScanner.getWrapperCollectionPairByHost (C:\Users\ostorero-gu\Documents\projects\oms-api\node_modules\@nestjs\core\injector\container-sca
nner.js:34:19)
    at ContainerScanner.findInstanceByToken (C:\Users\ostorero-gu\Documents\projects\oms-api\node_modules\@nestjs\core\injector\container-scanner.js:20:
40)
    at Object.findInstanceByToken (C:\Users\ostorero-gu\Documents\projects\oms-api\node_modules\@nestjs\core\injector\module-ref.js:40:38)
    at Object.get (C:\Users\ostorero-gu\Documents\projects\oms-api\node_modules\@nestjs\core\injector\module.js:341:29)
    at TypeOrmCoreModule.<anonymous> (C:\Users\ostorero-gu\Documents\projects\oms-api\node_modules\@nestjs\typeorm\dist\typeorm-core.module.js:94:47)   
    at Generator.next (<anonymous>)
    at C:\Users\ostorero-gu\Documents\projects\oms-api\node_modules\@nestjs\typeorm\dist\typeorm-core.module.js:20:71
    at new Promise (<anonymous>)
    at __awaiter (C:\Users\ostorero-gu\Documents\projects\oms-api\node_modules\@nestjs\typeorm\dist\typeorm-core.module.js:16:12)
    at TypeOrmCoreModule.onApplicationShutdown (C:\Users\ostorero-gu\Documents\projects\oms-api\node_modules\@nestjs\typeorm\dist\typeorm-core.module.js
:90:16)
(node:10136) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without
 a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:10136) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will term
inate the Node.js process with a non-zero exit code.

相关代码片段

AppModule配置

@Module({
  imports: [
    LoggerModule,
    ConfigurationModule,
    HealthcheckModule,
    TypeOrmModule.forRootAsync({
      imports: [ConfigurationModule],
      inject: [ConfigurationService],
      useFactory: async (configurationService: ConfigurationService) => {
        return ({
          type: 'oracle',
          extra: {
            connectString: configurationService.get(ConfigurationKeys.GOLDCONNECTSTRING),
          },
          username: configurationService.get(ConfigurationKeys.GOLDUSERNAME),
          password: configurationService.get(ConfigurationKeys.GOLDPASSWORD),
          database: '',
          entities: [__dirname + '/**/*.entity{.ts,.js}'],
          synchronize: false,
          logging: false,
        }) as TypeOrmModuleOptions;
      },
    }),
    HealthcheckTypeormModule.register({
      name: 'GOLD',
      type: 'oracle',
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigurationModule],
      name: 'reflex',
      inject: [ConfigurationService],
      useFactory: async (configurationService: ConfigurationService) => {
        return ({
          type: 'postgres',
          host: configurationService.get(ConfigurationKeys.REFLEXHOST),
          database: configurationService.get(ConfigurationKeys.REFLEXDB),
          username: configurationService.get(ConfigurationKeys.REFLEXUSERNAME),
          password: configurationService.get(ConfigurationKeys.REFLEXPASSWORD),
          synchronize: false,
          logging: false,
        }) as TypeOrmModuleOptions;
      },
    }),
    OmsModule,
  ],
  providers: []
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(HelmetMiddleware).forRoutes('*');
  }
}

ReflexRepository代码

@Injectable()
export class ReflexRepository {

  private static readonly TAG = 'ReflexRepository';

  constructor(
    @InjectConnection('reflex') private readonly connection: Connection,
    private readonly logger: LoggerService,
  ) {
  }

  public async getOrderPreparation(orderPreparationNumber: string): Promise<Array<ReflexModelOrderPreparation>> {

    let queryResult;
    const query = ReflexRepository.getOrderPreparationSql(orderPreparationNumber);

    try {
      queryResult = await this.connection.query(query);
    } catch (e) {
      this.logger.error(ReflexRepository.TAG, e.message);
      throw e;
    }

    return queryResult;
  }

  private static getOrderPreparationSql(orderPreparationNumber: string): string {
    return `private query`;
  }

  async getTrackingByOrderNumber(orderNumber: string): Promise<TrackingCommandeEntity[]> {
    const result = this.connection.getRepository(TrackingCommandeEntity).find({ NUM_CDE_CLT: orderNumber });
    if ((await result).length <= 0) { throw new NotFoundError('Tracking pour la commande introuvable'); }
    return result;
  }
}

OmsModule配置

@Module({
  imports: [ProduitsModule],
  controllers: [OmsController],
  providers: [OmsService, GoldRepository, OmsMapper, ReflexRepository],
})

export class OmsModule {
}

openapi.ts代码

async function run() {
  const logger = new LoggerService();
  const app = await NestFactory.create(AppModule, { logger });
  const outputPath = path.resolve(process.cwd(), 'openapi.yaml');

  const document = createDocument(app, true);

  if (!document.definitions) document.definitions = {};

  try {
    writeFileSync(outputPath, toYaml(document, { schema: JSON_SCHEMA }), { encoding: 'utf8' });
  } catch (e) {
    logger.error(`Error while creation yaml openapi : ${e.message}`);
  }

  await app.close();
}

run();

解决方案

方案1:模拟数据库连接,跳过实际初始化

生成OpenAPI文档无需实际数据库连接,可使用NestApplicationContext并替换Repository为模拟对象,避免连接初始化:

async function run() {
  const logger = new LoggerService();
  const appContext = await NestFactory.createApplicationContext(AppModule, { logger });
  
  // 模拟ReflexRepository的方法,避免依赖实际数据库连接
  appContext.select(OmsModule).overrideProvider(ReflexRepository).useValue({
    getOrderPreparation: () => Promise.resolve([]),
    getTrackingByOrderNumber: () => Promise.resolve([]),
  });

  const outputPath = path.resolve(process.cwd(), 'openapi.yaml');
  const document = createDocument(appContext, true);

  if (!document.definitions) document.definitions = {};

  try {
    writeFileSync(outputPath, toYaml(document, { schema: JSON_SCHEMA }), { encoding: 'utf8' });
  } catch (e) {
    logger.error(`Error while creation yaml openapi : ${e.message}`);
  }

  await appContext.close();
}

run();

方案2:统一Oracle连接的名称配置

主Oracle连接未指定name,默认使用'default',但Healthcheck模块注册的连接name为'GOLD',导致shutdown时连接实例不匹配。修改主连接配置,显式指定name:

TypeOrmModule.forRootAsync({
  name: 'GOLD', // 添加该行,与Healthcheck模块的连接名称统一
  imports: [ConfigurationModule],
  inject: [ConfigurationService],
  useFactory: async (configurationService: ConfigurationService) => {
    return ({
      type: 'oracle',
      extra: {
        connectString: configurationService.get(ConfigurationKeys.GOLDCONNECTSTRING),
      },
      username: configurationService.get(ConfigurationKeys.GOLDUSERNAME),
      password: configurationService.get(ConfigurationKeys.GOLDPASSWORD),
      database: '',
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      synchronize: false,
      logging: false,
    }) as TypeOrmModuleOptions;
  },
}),

方案3:手动销毁数据库连接后再关闭应用

在调用app.close()前,手动获取并销毁所有数据库连接,避免Nest在shutdown时找不到连接实例:

async function run() {
  const logger = new LoggerService();
  const app = await NestFactory.create(AppModule, { logger });
  const outputPath = path.resolve(process.cwd(), 'openapi.yaml');

  const document = createDocument(app, true);

  if (!document.definitions) document.definitions = {};

  try {
    writeFileSync(outputPath, toYaml(document, { schema: JSON_SCHEMA }), { encoding: 'utf8' });
  } catch (e) {
    logger.error(`Error while creation yaml openapi : ${e.message}`);
  }

  // 手动销毁所有数据库连接
  const connectionManager = app.get(ConnectionManager);
  await Promise.all(
    connectionManager.connections.map(conn => conn.destroy())
  );

  await app.close();
}

run();

内容的提问来源于stack exchange,提问作者Guillaume Ostorero

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 19:23:09