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

Nest Commander自定义命令调用时导入服务找不到的问题排查

Nest Commander 调用服务时模块找不到的问题解决

我有一个WebscraperModule模块,里面包含WebscraperService服务。在WebscraperService中导入了GeoCodingModule模块的GeoCodingService服务。我已经在GeoCodingModule里导出了GeoCodingService,也把GeoCodingModule导入到了WebscraperModule中,而且IDE里导入路径能正常跳转对应文件。但用Nest Commander创建的自定义命令调用WebscraperService时,报错:Error: Cannot find module 'src/@geoCoding/services/geoCoding.service'。运行npm run start:dev时完全正常,怀疑是Commander包配置问题,该怎么解决?

相关代码

Command 模块

@Module({
    providers: [ WebscraperCommand ],
    imports:   [ WebscraperModule ],
    exports:   [ WebscraperCommand ]
})
export class CommandsModule {}

Webscraper.command.ts

import { WebscraperService } from 'src/@webScraper/services/webscraper.service';

@Command({ name: 'basic', description: 'A parameter parse' })
export class WebscraperCommand extends CommandRunner {

    constructor(private readonly webscraperService: WebscraperService) {
        super();
    };
      
    async run() : Promise<void> {
      console.log("run")
        this.callMainScrapper();
    };

    @Option({
      flags: '-msc',
      description: 'Call the main scraper',
    })

    callMainScrapper(){     
     console.log("run");
     this.webscraperService.mainScraper();
    };

};

GeoCoding Module

@Global()
@Module({
    imports: [ HttpModule, ConfigModule.forRoot({ isGlobal: true }) ],
    providers: [ GeoCodingService ],
    exports: [ GeoCodingService ]
})
export class GeoCodingModule {}

GeoCoding Service

@Injectable()
export class GeoCodingService {

    constructor( 
      private readonly _httpService: HttpService, 
      private readonly _configService: ConfigService 
      ) {};

    public coordinates : object = {};
    
    private _logger = new Logger('GeoCodingService');             
    private _apiKey : string = this._configService.get('API_KEY');

    public async getGeoCoding(address: string): Promise<void> {

        const result : unknown = address.match(/^[A-Z]{1,2}[0-9]{1,2} ?[0-9][A-Z]{2}$/i);

        const url : string = `https://api.tomtom.com/search/2/geocode/${result[0].toUpperCase()}&limit=1.json?key=${this._apiKey}`;

        let res : object = {};

        try {

          this._httpService.get(url).then(response => {

            if (response.data.results[0].position === undefined) {
  
              res = {};
  
            } else {
  
              res = response.data.results[0].position;

            };
  
            this.coordinates = res;
  
          });

        } catch (error: any) {

          this._logger.error(error.message, error.stack);

        };
    };
};

Webscraper Module

@Global()
@Module({
    imports: [ GeoCodingModule, HttpModule, ConfigModule.forRoot({
      isGlobal: true
    }), ],
    exports: [ WebscraperService],
    providers: [WebscraperService, WebscraperCommand],
})
export class WebscraperModule {};

Webscraper Service

import { GeoCodingService } from 'src/@geoCoding/services/geoCoding.service';
// 报错指向这一行

export class WebscraperService {

  constructor( 
    private _geoCodingService: GeoCodingService,
    private readonly _httpService: HttpService, 
    private readonly _configService: ConfigService
    ) {};


 private async _getDataFromPage() : Promise<void> {

      for (const anchorLink of this._anchors) {
    
        const response : any = await fetch(anchorLink);
    
        const body : any = await response.text();

        this._setPropertyType(anchorLink);
    
        this._setProps(body);

        const slice: Array<string> = this._addressData.slice(0, -2);

        const addressToString = slice.toString()


        // 这里调用getGeoCoding
        this._geoCodingService.getGeoCoding(addressToString)


        
        const property : PropertyInterface = {
          address: addressToString,
          telephone: this._telephoneNumber,
          website: this._websiteLink,
          canLeaveDogsUnattended: this._dogsUnattended,
          maxDogs: this._numberOfDogs,
          extraCharge: this._charge,
          coordinates: this._coordinates,
          type: this._type
        };
  
      properties.push(property); 

      console.log(property, "property")

      await this._sleep(1000);
      
      };
    
    };


}

问题原因及解决办法

核心原因

Nest Commander执行命令时默认使用未编译的TS文件,而Node.js无法直接识别src/开头的绝对导入路径——这种路径通常靠TS配置的paths映射实现,但Node运行时不读取tsconfig.json的路径映射规则,导致模块查找失败。

解决步骤

  1. 确认TS路径映射配置
    确保tsconfig.json里已经配置了paths映射src/路径,示例:

    {
      "compilerOptions": {
        "baseUrl": "./",
        "paths": {
          "src/*": ["src/*"]
        }
      }
    }
    
  2. 让Node识别TS路径映射
    在命令启动脚本中添加tsconfig-paths/register参数,让Node加载TS的路径映射规则。修改package.json的命令:

    {
      "scripts": {
        "command": "ts-node --require tsconfig-paths/register src/main.ts --basic"
      }
    }
    

    如果用Nest CLI的nest start命令,需加上--exec参数:

    {
      "scripts": {
        "command": "nest start --exec 'ts-node --require tsconfig-paths/register' -- --basic"
      }
    }
    
  3. 移除重复的命令注册
    你已经在CommandsModule中注册了WebscraperCommand,又在WebscraperModule的providers里重复添加,会导致重复实例化引发依赖问题。修改WebscraperModule:

    @Global()
    @Module({
        imports: [ GeoCodingModule, HttpModule, ConfigModule.forRoot({
          isGlobal: true
        }), ],
        exports: [ WebscraperService],
        providers: [WebscraperService], // 移除WebscraperCommand
    })
    export class WebscraperModule {};
    
  4. 避免全局模块重复初始化
    ConfigModule.forRoot({ isGlobal: true })在GeoCodingModule和WebscraperModule都注册了,会导致重复初始化。建议只在根模块或单一模块中注册全局ConfigModule。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 21:10:18