如何基于Async Config创建Nestjs模块?
我正在尝试创建一个类似的DB模块:
const dbProvider = {
provide: 'DB',
useFactory: async (configService:ConfigService) => {
const dbUrl = configService.get<string>('DB_URL')
return Knex({
client: 'pg',
connection: dbUrl
})
},
inject: [ConfigService]
}
@Module({
providers: [ConfigService, dbProvider],
exports: [dbProvider],
})
export class DbModule {}
这是AppModule定义:
@Module({
controllers: [AppController],
providers: [Logger, AppService, {
provide: ConfigService,
useFactory: getConfigFactory(['DB_URL']),
}],
exports: [ConfigService]
})
export class AppModule {}
and:
export function getConfigFactory(paramsToLoad: string[]) {
return async () => {await getConfigService(paramsToLoad)}
}
export async function getConfigService(paramsToLoad: string[]) {
const paramStoreParams = await loadParamStore(paramsToLoad)
return new ConfigService(paramStoreParams)
}
loadParamStore使用SSM从SSM获取参数,
问题是,当执行DB设置时(上图),ConfigService仅包含所采集的ENVS从.env,DB_URL仅在以后的阶段加载(经过验证),因此在构建KNEX时,db_url尚未可用。
是否有正确的Nestjs实现此类功能的方法?
I'm trying to create a DB module like so:
const dbProvider = {
provide: 'DB',
useFactory: async (configService:ConfigService) => {
const dbUrl = configService.get<string>('DB_URL')
return Knex({
client: 'pg',
connection: dbUrl
})
},
inject: [ConfigService]
}
@Module({
providers: [ConfigService, dbProvider],
exports: [dbProvider],
})
export class DbModule {}
This is the AppModule definition:
@Module({
controllers: [AppController],
providers: [Logger, AppService, {
provide: ConfigService,
useFactory: getConfigFactory(['DB_URL']),
}],
exports: [ConfigService]
})
export class AppModule {}
and:
export function getConfigFactory(paramsToLoad: string[]) {
return async () => {await getConfigService(paramsToLoad)}
}
export async function getConfigService(paramsToLoad: string[]) {
const paramStoreParams = await loadParamStore(paramsToLoad)
return new ConfigService(paramStoreParams)
}
loadParamStore uses SSM to fetch parameters from SSM
The issue is, that when the DB setup is performed (above), the ConfigService only contains the envs taken from .env, DB_URL is only loaded at a later stage (verified), so at the time of building knex, DB_URL is not yet available.
Is there a correct Nestjs way to achieve such functionality?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
首先,首先删除您的自定义
getConfigFactory()
和getConfigservice()
函数。您不应自己创建configservice
实例。通过导入configmodule
,它可以为您实例化。当然,您可以自己启动并传递数据,但这是configmodule
的内部用法。如果要从外部源加载配置,请在您的情况下进行SSM,请使用自定义配置文件功能。
https://docs.nestjs.coms.coms.com/techniq.s.com/techniques/techniques/configuration#custom-confom-configuratire-files-files
添加一个新文件,例如
外部config.ts
到您的项目。在这里,您可以编码如何加载外部配置。它应该返回返回配置对象的出厂功能。顺便说一句,这仍然可以与
.env
文件结合使用。接下来,将
configModule
导入为AppModule
中的全局模块。这样,它的提供商(configservice
)可以在其他模块中使用,而无需重新构成configmodule
。使用其forroot()
方法,并指定load
选项中早期创建的自定义出厂功能。最后但并非最不重要的一点是将
configService
注入返回knex
实例的出厂功能。那时,您应该能够读取配置configservice
已为您读取。只需确保使用正确的点符号来访问您自定义出厂功能返回的配置对象的属性即可。如果运行该应用程序,则在启动过程中会注意到1S延迟,当“获取”配置时,在其模拟延迟时会延迟。
现在,您可以注入
db
令牌标识的提供商,这些提供商是appModule
的一部分的提供商。First off, start by removing your custom
getConfigFactory()
andgetConfigService()
functions. You should not create theConfigService
instance yourself. It is instantiated for you by importing theConfigModule
. Sure, you can new it up yourself and pass it data, but that's meant for internal usage by theConfigModule
.If you want to load configuration from an external source, SSM in your case, then use the custom configuration file feature.
https://docs.nestjs.com/techniques/configuration#custom-configuration-files
Add a new file, e.g.
external-config.ts
to your project. Here you can code how to load your external configuration. It should return a factory function that returns a configuration object.By the way, this can still be combined with
.env
files.Next import the
ConfigModule
as a global module in theAppModule
. This way its providers (ConfigService
) can be used in other modules without having to re-import theConfigModule
. Use itsforRoot()
method and specify the custom factory function created earlier in theload
option.Last, but not least inject the
ConfigService
in the factory function that returns theKnex
instance. At that time you should be able to read the configuration theConfigService
has read for you. Just make sure to use the correct dotted notation to access the properties of the configuration object returned by your custom factory function.If you run the application you'll notice a 1s delay during the startup when its simulating a delay when "fetching" the configuration.
You can now inject the provider identified by the
DB
token in your providers that are part of theAppModule
.所以我遇到了类似的问题。我的问题陈述是 - nest js中的AWS秘密。这必须在实例化其他模块之前完成。示例 - typeorm,JWT模块都需要配置值。
我是怎么实现的?通过构建一个辅助功能,该功能从AWS获取秘密并在过程中持续存在这些
值
。导入并运行此功能。
干杯!
PS-
我尝试在“加载”中使用Config Factory方法。但是它不起作用,因为在配置模块之前初始化了一些模块。
So I faced a similar problem. My problem Statement was - Load AWS Secrets in Nest JS. This had to be done before any other module is instantiated. Example - TypeORM, JWT Module both required config values.
How I achieved it? By building a helper function which fetches secrets from AWS and persists those values in process.env
You just need to set 2 properties in env files - 'AWS_REGION' and 'AWS_SECRET_ID'
Next, in the main.ts file. Import and Run this function.
Cheers!
PS -
I tried using config factory method in 'load'. But it doesn't work since some modules initialised before config module.
我遇到了一个类似的问题,并且是通过创建具有异步init函数的配置的类来解决此问题的ABOE。
此异步INIT函数将从
main.ts
文件中调用,这可能是异步的。这样,当将配置模块加载到应用程序模块(或app.module导入的模块)时,该配置已经在类的实例上。应用模块:
main.ts:
I faced a similar problem, and was aboe to solve this by creating a class that holds the config with an async init function.
this async init function will be called from the
main.ts
file, which can be asynchronous. This way, when the config module is loaded to app module (or to a module that app.module imports), the configuration is already on the class's instance.app module:
main.ts:
因此,@eedoh Kabelly的答案很好。我的用例是从 aws Secrets Manager 中获取秘密。但是,我还想要
.env
文件值的后备。如果.env
文件包含密钥,则应使用它;否则,它将使用AWS Secrets Manager键。基于此,我创建了aws-secrets-config.ts
文件。fetch-aws-secrets.ts
在
main.ts
So the answer by @Eedoh Kabelly is good. My use case was to fetch secrets from AWS Secrets Manager. However, I also wanted a fallback for
.env
file values. If the.env
file contains the key, it should use that; otherwise, it will use the AWS Secrets Manager Keys. Based on this, I created theaws-secrets-config.ts
file.fetch-aws-secrets.ts
Call the init function before creation of Nest app in the
main.ts
Load the configuration in the
ConfigModule
withinapp.module.ts
. Please note thatload
will only allow synchronous configurations to be loaded. That's why it is important to load the secrets inmain.ts
before theConfigModule
attempts to load them.Your Database Module can now use the secret directly from confService
In your
.env
file, you only need to include:AWS_REGION
AWS_SECRET_NAME
If you are not using an instance role or are testing locally, you will also need to add:
AWS_ACCESS_KEY
AWS_SECRET_KEY
I’ve tested this setup and have successfully accessed the confService in other parts of the app; it works perfectly. The logic is to load the secrets before the Nest app is created because the ConfigModule does not support forRootAsync, and the load function does not allow for asynchronous configuration loading.
If you need the secrets to be fetched dynamically at runtime, you can directly invoke
fetchSecretsFromAWS()
in your app to get the latest values.