【问题标题】:NestJs TypeORM configuration using AWS Parameter StoreNestJs TypeORM 配置使用 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 的任何内容。 StackOverflow 上也有一个现有的(未回答的)问题。

谢谢!

【问题讨论】:

    标签: node.js typescript 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

    【讨论】:

      猜你喜欢
      • 2019-06-19
      • 2019-10-31
      • 2019-05-10
      • 2022-01-17
      • 2022-06-22
      • 2020-08-22
      • 2018-08-26
      • 2020-05-11
      • 2020-12-20
      相关资源
      最近更新 更多