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

NestJS中useFactory内调用Sequelize模型报错,求解决方案

能否在NestJS的useFactory中操作模型?

问题描述

尝试在NestJS的PuppeteerModule.forRootAsync的useFactory中调用ProxyService获取数据库中的代理配置,运行时出现错误:

[Nest] ERROR [ExceptionHandler] Model not initialized: Member "findOne" cannot be called. "ProxyEntity" needs to be added to a Sequelize instance.

该模型在应用其他业务代码中可正常使用,仅在useFactory场景下报错。

相关代码见问题原文。

错误原因

useFactory是在NestJS模块初始化的引导阶段执行的,此时Sequelize的模型还未完成与数据库实例的关联初始化——虽然导入了DatabaseModule,但模块初始化的顺序和依赖解析逻辑,会导致模型还未被Sequelize实例加载绑定,因此无法调用findOne等方法。而应用其他部分的调用是在应用启动完成后,所有模型已完成初始化,所以可以正常工作。

可行解决方案

方案1:使用APP_INITIALIZER提前加载代理配置

通过APP_INITIALIZER在应用初始化的后期阶段(所有模块注册完成后)获取代理配置,确保模型已初始化,再将配置传递给PuppeteerModule。

  1. 创建代理配置服务,用于存储和初始化代理参数:
// proxy-config.service.ts
@Injectable()
export class ProxyConfigService {
  proxyOptions: string[] = [];

  constructor(private readonly proxyService: ProxyService) {}

  async init() {
    const proxy = await this.proxyService.get();
    // 构造Puppeteer所需的代理启动参数
    this.proxyOptions = [`--proxy-server=${proxy.ip}:${proxy.port}`];
    // 处理代理认证(如果有)
    if (proxy.username && proxy.password) {
      this.proxyOptions.push(`--proxy-auth=${proxy.username}:${proxy.password}`);
    }
  }
}
  1. 修改ProxyModule,注册APP_INITIALIZER触发配置初始化:
// proxy.module.ts
import { APP_INITIALIZER } from '@nestjs/core';

@Module({
  imports: [DatabaseModule],
  providers: [
    ...ProxiesProviders,
    ProxyService,
    ProxyConfigService,
    {
      provide: APP_INITIALIZER,
      useFactory: (proxyConfigService: ProxyConfigService) => () => proxyConfigService.init(),
      inject: [ProxyConfigService],
      multi: true,
    },
  ],
  exports: [ProxyService, ProxyConfigService],
})
export class ProxyModule {}
  1. 修改CrawlersModule的useFactory,注入配置服务获取代理参数:
// crawlers.module.ts
@Module({
  imports: [
    PuppeteerModule.forRootAsync({
      imports: [ProxyModule],
      useFactory: async (proxyConfigService: ProxyConfigService) => {
        return {
          launchOptions: {
            args: [
              '--no-sandbox',
              '--disable-setuid-sandbox',
              '--mute-audio',
              ...proxyConfigService.proxyOptions,
            ],
          },
        };
      },
      inject: [ProxyConfigService],
    }),
  ],
})
export class CrawlersModule {}

方案2:动态创建Puppeteer实例(推荐)

不在模块初始化阶段固定代理配置,而是在业务服务中每次创建浏览器实例时动态获取代理,彻底避开模块初始化阶段的模型未初始化问题。

  1. 简化CrawlersModule的Puppeteer配置:
// crawlers.module.ts
@Module({
  imports: [
    PuppeteerModule.forRoot({
      launchOptions: {
        args: ['--no-sandbox', '--disable-setuid-sandbox', '--mute-audio'],
      },
    }),
    ProxyModule,
  ],
  providers: [CrawlerService],
})
export class CrawlersModule {}
  1. 在业务服务中动态创建带代理的浏览器实例:
// crawler.service.ts
@Injectable()
export class CrawlerService {
  constructor(
    private readonly puppeteerService: PuppeteerService,
    private readonly proxyService: ProxyService,
  ) {}

  async createBrowserWithProxy() {
    const proxy = await this.proxyService.get();
    // 合并基础配置与代理参数
    const launchOptions = {
      ...this.puppeteerService.launchOptions,
      args: [
        ...this.puppeteerService.launchOptions.args,
        `--proxy-server=${proxy.ip}:${proxy.port}`,
      ],
    };
    const browser = await this.puppeteerService.launch(launchOptions);

    // 处理代理认证(如果需要)
    if (proxy.username && proxy.password) {
      const page = await browser.newPage();
      await page.authenticate({
        username: proxy.username,
        password: proxy.password,
      });
    }

    return browser;
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 11:30:42