NestJs:无法读取模块文件中的环境变量但可以读取服务文件中的变量?

NestJs: Unable to read env variables in module files but able in service files?

我的 NestJs 项目的根目录下有一个 .env 文件,其中包含一些环境变量。

奇怪的是,我可以读取服务文件中的变量,但不能读取模块文件中的变量。

所以在像 users.service.ts 这样的服务文件中,这有效:

saveAvatar() {
    const path = process.env.AVATAR_PATH    // returns value from .env
}

但是,当访问像 auth.module.ts 这样的模块文件中的路径时,这个 returns 是一个空值:

@Module({
    imports: [
       JwtModule.register({
          secretOrPrivateKey: process.env.SECRET   // process.env.SECRET returns an empty string
       })
    ]
})

为什么会这样?如何在 NestJs 中可靠地访问 .env 文件中的环境变量?

当您的 JwtModule 实例化时,您的 .env 文件尚未读入。因此,要么早点阅读它,例如在你的 main.ts 之前创建 nest 应用程序或更好:创建一个 ConfigService 并明确依赖你的配置:

JwtModule.registerAsync({
    imports: [ConfigModule],
    useFactory: async (configService: ConfigService) => ({
      secret: configService.jwtSecret,
    }),
    inject: [ConfigService],
}),

请参阅 了解如何创建 ConfigService

您可以将其用作全局以供其他模块访问。

When you want to use ConfigModule in other modules, you'll need to import it (as is standard with any Nest module). Alternatively, declare it as a global module by setting the options object's isGlobal property to true, as shown below. In that case, you will not need to import ConfigModule in other modules once it's been loaded in the root module (e.g., AppModule)

.https://docs.nestjs.com/techniques/configuration#use-module-globally

ConfigModule.forRoot({
  isGlobal: true
});

您还可以在这里找到如何使用配置服务:https://docs.nestjs.com/techniques/configuration#using-the-configservice

正如@KimKen 所说,问题在于,在实例化 JwtModule 时,环境变量仍未加载。但是,对于@KimKen 的回答,我有不同的方法,您可能也会感兴趣。

首先,NestJS 提供了一个加载环境变量的 ConfigModule,所以你不需要创建一个,除非你希望以不同于通常的方式处理它。 (https://docs.nestjs.com/techniques/configuration)

现在,为了解决这个问题,我将模块 (auth.module.ts) 动态化。 简而言之,动态模块是接收参数的模块,它依赖于输入参数来正确实例化。(https://docs.nestjs.com/fundamentals/dynamic-modules)

这里真正发生的事情是 JwtModule 也是一个动态模块,因为它依赖于一个变量来正确实例化。因此,这也导致您的模块也依赖于其正确实例化的参数,因此使其动态化! :).

那么您的 auth.module 将类似于:

@Module({})
export class AuthModule {

    static forRoot(): DynamicModule {
        return {
            imports: [
                JwtModule.register({
                    secretOrPrivateKey: process.env.SECRET   // process.env.SECRET will return the proper value
                })
            ],
            module: AuthModule
        }
    }

然后它将像在您的 app.module 中一样简单,或者您在任何地方加载 auth.module 通过 forRoot 静态方法导入它。

import { ConfigModule } from '@nestjs/config';

@Module({
    imports: [ConfigModule.forRoot(), AuthModule.forRoot()]
})

注意:我建议在 app.module.ts

中导入一次 ConfigModule

PD:您可以让动态 auth.module 在 forRoot 方法中接收参数,并在 app.module 中传递环境变量 process.env.SECRET

AuthModule.forRoot(process.env.SECRET)

但似乎动态模块是最后加载的,所以没有必要。

我刚刚意识到我也解决了我的问题,只需导入 dotenv 并在我的模块开头调用方法 config (auth.module.ts)。

import dotenv from "dotenv";

dotenv.config({path:<path-to-env-file>})

如果您使用项目根目录下的默认 .env 文件,则无需指定路径。

声明顺序在您的用例中很重要。

这个有效:

@Module({
  imports: [
    ConfigModule.forRoot(),
    ScheduleModule.forRoot(),
    TypeOrmModule.forRoot({
      type: 'mongodb',
      host: process.env.SYN_MONGO_HOST,
      port: +process.env.SYN_MONGO_PORT,
      username: process.env.SYN_MONGO_USERNAME,
      password: process.env.SYN_MONGO_PASSWORD,
      database: process.env.SYN_MONGO_DATABASE,
      authSource: 'admin',
      autoLoadEntities: true,
    }),
  ],
  controllers: [],
  providers: [],
})
export class ConfigurationModule {}

当这不是

@Module({
  imports: [
    ScheduleModule.forRoot(),
    TypeOrmModule.forRoot({
      type: 'mongodb',
      host: process.env.SYN_MONGO_HOST,
      port: +process.env.SYN_MONGO_PORT,
      username: process.env.SYN_MONGO_USERNAME,
      password: process.env.SYN_MONGO_PASSWORD,
      database: process.env.SYN_MONGO_DATABASE,
      authSource: 'admin',
      autoLoadEntities: true,
    }),
    ConfigModule.forRoot(),
  ],
  controllers: [],
  providers: [],
})
export class ConfigurationModule {}

这是因为ConfigModule是在TypeOrmModule之前或之后加载的。