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:无法读取模块文件中的环境变量但能够读取服务文件?的主要内容,如果未能解决你的问题,请参考以下文章