NestJs TypeORM 配置使用 AWS Parameter Store

Posted

技术标签:

【中文标题】NestJs TypeORM 配置使用 AWS Parameter Store【英文标题】:NestJs TypeORM configuration using AWS Parameter Store 【发布时间】:2019-09-18 13:30:10 【问题描述】:

NestJS 新手,遇到了一个问题。对于我们的部署,我们需要从 AWS Parameter Store (Systems Manager) 获取我们的配置,包括数据库连接字符串。我有一个 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 秒,因此“发生 undefined” 错误是因为当 TypeORM 尝试加载连接字符串时“this.awsParamStoreEntries”仍未定义。

已在网上搜索以查看是否已完成此操作,但找不到以这种方式使用 NestJS / TypeORM / AWS Parameter Store 的任何内容。 *** 上也有一个现有的(未回答的)问题。

谢谢!

【问题讨论】:

【参考方案1】:

我们可以使用异步提供程序。

使用 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[],
  ) 

编辑:提供在应用程序启动时加载 aws 参数存储参数的服务的 NPM 包:

param-store-service

【讨论】:

你有这个代码的完整示例吗? @Metallikanz 我已经编辑了我的答案。看看 NPM 包。您可以按原样使用它,也可以查看 GitHub 代码库。【参考方案2】:

你能做这样的事情吗?

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) ...

https://docs.nestjs.com/techniques/database

【讨论】:

以上是关于NestJs TypeORM 配置使用 AWS Parameter Store的主要内容,如果未能解决你的问题,请参考以下文章

NestJs TypeORM 异步配置

如何在 Nestjs 中使用 .env 文件设置 Typeorm 的配置

运行nestjs应用程序时typeorm迁移中的“不能在模块外使用import语句”

NestJS:通过请求(子域)连接数据库(TypeORM)

NestJS typeorm - 无法创建实体

找不到模块'@nestjs/typeorm'