NestJS的新功能,遇到了一个问题。对于我们的部署,我们需要从AWS Parameter Store(系统管理器)获取配置,包括数据库连接字符串。我有一个ConfigModule和ConfigService,它根据参数存储路径为我的环境检索所有参数存储条目:
这是我的配置服务:
import * as dotenv from 'dotenv';
import * as fs from 'fs';
import * as AWS from 'aws-sdk';
export class ConfigService {
private readonly envConfig: { [key: string]: string };
private awsParamStoreEntries: { [key: string]: string }[];
constructor(awsParamStorePath: string, filePath: string) {
this.envConfig = dotenv.parse(fs.readFileSync(filePath));
this.loadAwsParameterStoreEntries(awsParamStorePath).then((data) => {
this.awsParamStoreEntries = data;
});
}
loadAwsParameterStoreEntries(pathPrefix: string) {
const credentials = new AWS.SharedIniFileCredentials({ profile: 'grasshopper-parameter' });
AWS.config.credentials = credentials;
const ssm = new AWS.SSM({ region: 'us-west-2' });
var params: { [key: string]: string }[] = [];
return getParams({
Path: '/app/v3/development/',
Recursive: true,
WithDecryption: true,
MaxResults: 10,
}).then(() => {
return params;
});
function getParams(options) {
return new Promise((resolve, reject) => {
ssm.getParametersByPath(options, processParams(options, (err, data) => {
if (err) {
return reject(err);
}
resolve(data);
}));
});
}
function processParams(options, cb) {
return function (err, data) {
if (err) {
return cb(err)
};
data.Parameters.forEach(element => {
let key = element.Name.split('/').join(':')
params.push({ key: key, value: element.Value });
});
if (data.NextToken) {
const nextOptions = Object.assign({}, options);
nextOptions.NextToken = data.NextToken;
return ssm.getParametersByPath(nextOptions, processParams(options, cb));
}
return cb(null);
};
}
}
get(key: string): string {
return this.envConfig[key];
}
getParamStoreValue(key: string): string {
return this.awsParamStoreEntries.find(element => element.key === key)['value'];
}
getDatabase(): string {
return this.awsParamStoreEntries.find(element => element.key === 'ConnectionStrings:CoreDb')['value'];
}
}
这是主要的应用模块声明块:
@Module({
imports: [ConfigModule, TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
url: configService.getDatabase()
}),
inject: [ConfigService]
}),
CoreModule, AuthModule],
controllers: [AppController],
providers: [AppService],
})
如您所见,我告诉TypeORM在ConfigService中调用getDatabase()方法,但问题是参数存储条目的加载大约需要3-4秒,因此“未定义”错误发生,因为当TypeORM尝试加载连接字符串时,“ this.awsParamStoreEntries”仍然未定义。
已在网络上进行搜索以查看是否已完成,但是找不到以这种方式使用NestJS / TypeORM / AWS参数存储的任何内容。在StackOverflow上也存在一个现有问题(未回答)。
谢谢!
答案 0 :(得分:0)
你能做这样的事情吗?
import { TypeOrmOptionsFactory, TypeOrmModuleOptions } from '@nestjs/typeorm';
import { ConfigService } from './config.service';
import { Injectable } from '@nestjs/common';
@Injectable()
export class TypeOrmConfigService implements TypeOrmOptionsFactory {
constructor(private readonly configService: ConfigService) {
}
async createTypeOrmOptions(): Promise<TypeOrmModuleOptions> {
this.configService.awsParamStoreEntries = await this.configService.loadAwsParameterStoreEntries(this.configService.awsParamStorePath);
return {
url: this.configService.getDatabase(),
};
}
}
@Module({
imports: [
ConfigModule,
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useClass: TypeOrmConfigService,
}),
CoreModule,
AuthModule
],
controllers: [AppController],
providers: [AppService],
})
import * as dotenv from 'dotenv';
import * as fs from 'fs';
import * as AWS from 'aws-sdk';
import { Injectable } from '@nestjs/common';
@Injectable()
export class ConfigService {
private readonly envConfig: { [key: string]: string };
awsParamStoreEntries: { [key: string]: string }[];
private readonly awsParamStorePath: string;
constructor(awsParamStorePath: string, filePath: string) {
this.envConfig = dotenv.parse(fs.readFileSync(filePath));
this.awsParamStorePath = awsParamStorePath;
}
loadAwsParameterStoreEntries(pathPrefix: string) {...
答案 1 :(得分:0)
我们可以使用异步提供程序。
来自一个工作项目的示例,用于将 AWS SSM 参数存储参数与 NestJS 配置服务结合使用:
创建异步提供程序:
import * as AWS from 'aws-sdk';
import { Parameter } from 'aws-sdk/clients/ssm';
export const ssmProvider = {
provide: 'AWS_SSM',
useFactory: async (): Promise<Parameter[]> => {
const ssmClient = new AWS.SSM({
endpoint: 'endpoint',
region: 'us-west-2',
});
const result = await ssmClient
.getParametersByPath({
Path: '/ssm/path',
Recursive: true,
})
.promise();
return result?.Parameters;
},
};
然后我们可以使用这个提供程序来注入值。
例如在配置服务中注入 AWS SSM 参数:
export class ConfigModule {
static register(options: ConfigModuleOptions): DynamicModule {
return {
global: true,
module: ConfigModule,
providers: [
ssmProvider,
{
provide: CONFIG_OPTIONS,
useValue: options,
},
ConfigService,
],
exports: [ConfigService],
};
}
}
在配置服务构造函数中:
constructor(
@Inject(CONFIG_OPTIONS) options: ConfigOptions,
@Inject('AWS_SSM') awsParameters: Parameter[],
) {}