NestJS中Mongoose Schema读取process.env返回undefined如何解决
问题根因
@Prop()装饰器内的代码会在文件导入、类加载阶段立即执行,这个时机早于ConfigModule.forRoot()解析.env文件并将配置挂载到process.env的时间,直接写process.env.NAME自然拿到undefined。- 现有
MongooseModule.forFeatureAsync写法没有注入配置依赖,也没有延迟Schema属性配置的初始化逻辑,直接返回了提前加载完成、默认值已经固化为undefined的Schema实例。 - PetsModule中重复导入
ConfigModule属于冗余代码,AppModule中已经开启isGlobal: true,配置服务全局可用。
修正步骤
- 先调整Schema文件,移除
@Prop()中静态写死的环境变量读取逻辑:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import { Document } from 'mongoose'; export type PetDocument = Pet & Document; @Schema() export class Pet { @Prop() name: string; @Prop() age: number; @Prop() breed: string; } export const PetSchema = SchemaFactory.createForClass(Pet);
- 修改PetsModule的异步注册逻辑,注入
ConfigService,在工厂函数中(此时配置模块已经完成初始化)动态给Schema字段设置默认值:
import { Module } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { MongooseModule } from '@nestjs/mongoose'; import { PetsService } from './pets.service'; import { Pet, PetSchema } from './schema/pets.schema'; @Module({ imports: [ MongooseModule.forFeatureAsync([ { name: Pet.name, useFactory: (configService: ConfigService) => { const defaultPetName = configService.get<string>('NAME'); PetSchema.path('name').default(defaultPetName); return PetSchema; }, inject: [ConfigService], }, ]), ], providers: [PetsService], }) export class PetsModule {}
- (规范建议)调整AppModule中数据库连接的配置写法,同样通过注入
ConfigService读取配置,避免偶发的时序问题:
import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { MongooseModule } from '@nestjs/mongoose'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { PetsModule } from './pets/pets.module'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, }), MongooseModule.forRootAsync({ useFactory: (configService: ConfigService) => ({ uri: configService.get<string>('DB_URI'), }), inject: [ConfigService], }), PetsModule, ], controllers: [AppController], providers: [AppService], }) export class AppModule {}
注意事项
- 禁止在类装饰器、属性装饰器的参数位置直接读取
process.env或调用需要依赖其他模块初始化的逻辑,装饰器执行时机远早于NestJS依赖注入容器的初始化流程,必然会出现依赖未就绪的问题。 - 所有依赖配置、其他服务的动态初始化逻辑,都要放到对应模块的异步工厂
useFactory中执行,并且通过inject数组声明依赖,NestJS会等待所有依赖初始化完成后再执行工厂逻辑,保证能拿到正确的值。
内容的提问来源于stack exchange,提问作者Otávio
相关产品推荐
相关产品推荐

