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

Posted

技术标签:

【中文标题】NestJs:无法读取模块文件中的环境变量但能够读取服务文件?【英文标题】:NestJs: Unable to read env variables in module files but able in service files? 【发布时间】:2019-09-04 12:07:50 【问题描述】:

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

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

所以在像users.service.ts 这样的服务文件中,这是可行的:

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

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

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

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

【问题讨论】:

您在哪里(以及何时)阅读.env 文件? @KimKern 我正在读取位于模块文件夹中的模块文件中的文件。 .env 文件位于项目的根目录中。我已经更新了我的问题以显示它们何时被使用。 【参考方案1】:

当您的JwtModule 被实例化时,您的.env 文件尚未读入。所以要么在前面阅读它,例如在您的 main.ts 创建嵌套应用程序之前或更好:创建一个 ConfigService 并明确对您的配置的依赖:

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

请参阅this answer,了解如何创建ConfigService

【讨论】:

【参考方案2】:

您可以将其用作全局以便从其他模块进行访问。

当您想在其他模块中使用 ConfigModule 时,您需要 导入它(对于任何 Nest 模块都是标准的)。或者, 通过设置选项对象的 isGlobal 将其声明为全局模块 属性为true,如下所示。在这种情况下,您无需 将 ConfigModule 加载到根目录后,将其导入其他模块 模块(例如,AppModule)

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

ConfigModule.forRoot(
  isGlobal: true
);

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

【讨论】:

@Kekson 设置后是否重新启动应用程序?请注意“ConfigModule 已加载到根模块(例如 AppModule)” 是但重启没有帮助,在我的 app.module.ts 我已经设置了这个 ConfigModule.forRoot( isGlobal: true ) 在其他模块中我有 process.env.PORT 但它仍然不从 env 读取 如果我将 ConfigModule.forRoot() 放在其他模块导入以及 AppModule 中(as isGlobal: true),那么它可以正常工作......看起来很奇怪,但它可以工作 @Kekson 其他模块应该是您的主模块的子模块。如果其他模块没有被定义,你可能重新定义它。【参考方案3】:

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

import "dotenv" from dotenv;

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

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

【讨论】:

【参考方案4】:

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

这行得通:

@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 之前或之后加载的。

【讨论】:

【参考方案5】:

就像@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)

但似乎动态模块是最后加载的,所以不需要。

【讨论】:

以上是关于NestJs:无法读取模块文件中的环境变量但能够读取服务文件?的主要内容,如果未能解决你的问题,请参考以下文章

.env 文件中的变量的 nestjs 问题

perl变量到模块

节点无法读取 AWS Beanstalk 中的环境变量

如何对一个扩展了抽象类的类进行单元测试,读取环境变量。

在 main.ts 中使用 .env 文件中的数据 - NestJS

nestJs 处理环境变量