生产环境启动Nest实例时出现导入数组异常错误
使用NODE_ENV=production nest build构建NestJS应用后,在MacBook本地及Vagrant模拟的生产环境中,通过NODE_ENV=production nest start可正常启动,但在运行同版本Node.js 16.20的生产机器上启动时报错:
[Nest] 1108076 - 06/12/2023, 9:54:51 PM LOG [NestFactory] Starting Nest application...
[Nest] 1108076 - 06/12/2023, 9:54:51 PM ERROR [ExceptionHandler] Nest cannot create the AppModule instance.
Received an unexpected value at index [3] of the AppModule "imports" array.Scope []
Error: Nest cannot create the AppModule instance.
Received an unexpected value at index [3] of the AppModule "imports" array.Scope []
at DependenciesScanner.scanForModules (/home/check-inventory/test/node_modules/@nestjs/core/scanner.js:56:23)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async DependenciesScanner.scan (/home/check-inventory/test/node_modules/@nestjs/core/scanner.js:27:9)
at async /home/check-inventory/test/node_modules/@nestjs/core/nest-factory.js:107:17
at async Function.asyncRun (/home/check-inventory/test/node_modules/@nestjs/core/errors/exceptions-zone.js:22:13)
at async NestFactoryStatic.initialize (/home/check-inventory/test/node_modules/@nestjs/core/nest-factory.js:106:13)
at async NestFactoryStatic.create (/home/check-inventory/test/node_modules/@nestjs/core/nest-factory.js:42:9)
at async bootstrap (/home/check-inventory/test/dist/src/main.js:13:17)
已通过scp传输本地构建的dist和node_modules至生产机器并调整权限,但问题仍未解决。
AppModule代码如下:
import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common"; import { AppController } from "./app.controller"; import { AppService } from "./app.service"; import { TypeOrmModule } from "@nestjs/typeorm"; import { ConfigModule } from "@nestjs/config"; import { UsersModule } from "./users/users.module"; import { GroupsModule } from "./groups/groups.module"; import { RequestsModule } from "./requests/requests.module"; import { CategoriesModule } from "./categories/categories.module"; import { ItemsModule } from "./items/items.module"; import { UserInjectMiddleware } from "./middleware/user-inject.middleware"; import { TrackerModule } from "./tracker/tracker.module"; import { ScheduleModule } from "@nestjs/schedule"; import { SerialsModule } from "./serials/serials.module"; import { SocketModule } from "./socket/socket.module"; import { StoragesModule } from "./storages/storages.module"; import { AuthModule } from "./auth/auth.module"; import { APP_GUARD } from "@nestjs/core"; import { AzureValidatorGuard } from "./auth/azure-validator.guard"; import { RolesGuard } from "./auth/roles.guard"; import { StatisticsModule } from "./statistics/statistics.module"; import { config } from "./dataSource.config"; const env = process.env.NODE_ENV || "production"; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, envFilePath: `../.env.${env}`, }), TypeOrmModule.forRoot(config), ScheduleModule.forRoot(), process.env.EASYPOST_APIKEY ? TrackerModule : null, UsersModule, GroupsModule, RequestsModule, CategoriesModule, ItemsModule, SerialsModule, StoragesModule, SocketModule, AuthModule, StatisticsModule, ], controllers: [AppController], providers: [ AppService, { provide: APP_GUARD, useClass: AzureValidatorGuard, }, { provide: APP_GUARD, useClass: RolesGuard, }, ], }) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer.apply(UserInjectMiddleware).forRoutes("*"); } }
问题出在AppModule的imports数组里的这一行:
process.env.EASYPOST_APIKEY ? TrackerModule : null,
当生产机器上未配置EASYPOST_APIKEY环境变量时,null会被注入到imports数组的第3位(索引从0开始),而NestJS的模块imports数组不允许存在null或undefined这类无效值,这就是启动报错的核心原因。本地环境能正常启动,大概率是因为本地已经配置了该环境变量,不会触发null的情况。
提供两种可行的解决方法:
- 方案一:过滤无效值
将imports数组改为动态生成的形式,自动过滤掉所有假值(null、undefined等):
@Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, envFilePath: `../.env.${env}`, }), TypeOrmModule.forRoot(config), ScheduleModule.forRoot(), ...(process.env.EASYPOST_APIKEY ? [TrackerModule] : []), UsersModule, GroupsModule, RequestsModule, CategoriesModule, ItemsModule, SerialsModule, StoragesModule, SocketModule, AuthModule, StatisticsModule, ].filter(Boolean), // 其余配置保持不变 })
这里用扩展运算符...将条件模块转为数组,再通过filter(Boolean)过滤掉所有无效值。
- 方案二:提前构建imports数组
先在模块外构建好完整的imports数组,再传入@Module装饰器:
const imports = [ ConfigModule.forRoot({ isGlobal: true, envFilePath: `../.env.${env}`, }), TypeOrmModule.forRoot(config), ScheduleModule.forRoot(), UsersModule, GroupsModule, RequestsModule, CategoriesModule, ItemsModule, SerialsModule, StoragesModule, SocketModule, AuthModule, StatisticsModule, ]; if (process.env.EASYPOST_APIKEY) { imports.splice(3, 0, TrackerModule); // 将模块插入到原位置 } @Module({ imports, // 其余配置保持不变 })
内容的提问来源于stack exchange,提问作者Daniel Terletzkiy

