Configify

repository·master·Indexed 42 days ago

https://github.com/it-gorillaz/configify

A NestJS configuration module (v4.1.2) that simplifies loading, validating, and injecting configuration from environment variables, config files, and remote secret managers. It provides built-in support for AWS Parameter Store, AWS Secrets Manager, Azure Key Vault, and Bitwarden Secrets Manager, and allows for the implementation of custom resolvers via the RemoteConfigurationResolverStrategy interface.

Tokens
12K
Snippets
42
Records
51
Agent score
52%

What's inside Configify

  1. How configuration file discovery works

    master

    By default, configify automatically looks for the following files in the root of your project:

    • .env
    • application.yml
    • application.json

    All three formats are supported and can be used simultaneously. You can override these default paths using the configFilePath option in forRootAsync().

  2. Supported Secrets Management providers

    master

    Configify supports resolving secrets from several external providers:

    • AWS Secrets Manager
    • AWS Parameter Store
    • Azure Key Vault
    • Bitwarden Secrets Manager
    • Google Cloud Secret Manager
    • Custom resolver (implement ConfigurationResolver)
  3. Configure Bitwarden Secrets Manager using the default resolver

    master

    You can use the BitwardenSecretsResolverFactory to quickly set up a resolver. By default, if no access token is provided to defaultBitwardenSecretsResolver(), the module will automatically look for the BWS_ACCESS_TOKEN environment variable.

    Import ConfigifyModule and use BitwardenSecretsResolverFactory.defaultBitwardenSecretsResolver() within the secretsResolverStrategies array of ConfigifyModule.forRootAsync().

    import { ConfigifyModule } from '@itgorillaz/configify';
    import {
      BitwardenSecretsResolverFactory,
      BitwardenServerRegion,
    } from '@itgorillaz/configify/configuration/resolvers/bitwarden';
    
    @Module({
      imports: [
        ConfigifyModule.forRootAsync({
          secretsResolverStrategies: [
            BitwardenSecretsResolverFactory.defaultBitwardenSecretsResolver(
              BitwardenServerRegion.EU,
            ),
          ],
        }),
      ],
      controllers: [AppController],
      providers: [AppService],
    })
    export class AppModule {}
  4. Validate configuration using class-validator

    master

    To ensure your application doesn't start with invalid settings, use class-validator decorators on your @Configuration() class properties. If validation fails, the application will throw an error during the startup sequence.

    import { IsEmail, IsNotEmpty, IsUrl } from 'class-validator';
    import { Configuration, Value } from '@itgorillaz/configify';
    
    @Configuration()
    export class NotificationConfiguration {
      @IsEmail()
      @Value('SENDER_EMAIL')
      senderEmail: string;
    
      @IsNotEmpty()
      @Value('API_TOKEN')
      apiToken: string;
    }
  5. Configure GoogleCloudSecretManagerConfigurationResolver

    master

    You can integrate GCP Secret Manager into your application using ConfigifyModule in two ways:

    Use GoogleCloudSecretsResolverFactory.defaultSecretManagerConfigurationResolver() for a standard setup.

    2. Using a custom SecretManagerServiceClient

    If you need to provide your own instance of SecretManagerServiceClient (e.g., for custom authentication or client configuration), wrap the GoogleCloudSecretManagerConfigurationResolver inside a RemoteConfigurationResolver.

    // Option 1: Default Factory
    import { ConfigifyModule } from '@itgorillaz/configify';
    import { GoogleCloudSecretsResolverFactory } from '@itgorillaz/configify/configuration/resolvers/gcp';
    
    @Module({
      imports: [
        ConfigifyModule.forRootAsync({
          secretsResolverStrategies: [
            GoogleCloudSecretsResolverFactory.defaultSecretManagerConfigurationResolver(),
          ],
        }),
      ],
    })
    export class AppModule {}
    
    // Option 2: Custom Client
    import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
    import { ConfigifyModule, RemoteConfigurationResolver } from '@itgorillaz/configify';
    import { GoogleCloudSecretManagerConfigurationResolver } from '@itgorillaz/configify/configuration/resolvers/gcp';
    
    @Module({
      imports: [
        ConfigifyModule.forRootAsync({
          secretsResolverStrategies: [
            new RemoteConfigurationResolver(
              new GoogleCloudSecretManagerConfigurationResolver(
                new SecretManagerServiceClient(),
              ),
            ),
          ],
        }),
      ],
    })
    export class AppModule {}
  6. Initialize ConfigifyModule in NestJS

    master

    Import ConfigifyModule into your root module using forRootAsync().

    Important for TypeScript Strict Mode: If you have "strict": true in your tsconfig.json and you are not using constructor configuration injection, you must set "strictPropertyInitialization": false. This is because configify initializes properties at runtime.

    {
      "compilerOptions": {
        "strict": true,
        "strictPropertyInitialization": false
      }
    }
    import { ConfigifyModule } from '@itgorillaz/configify';
    
    @Module({
      imports: [ConfigifyModule.forRootAsync()],
      controllers: [AppController],
      providers: [AppService],
    })
    export class AppModule {}
  7. Define a custom configuration resolver

    master

    To fetch secrets from a proprietary or custom source, implement the RemoteConfigurationResolverStrategy interface.

    Your implementation must define:

    1. configurationKeys: A readonly string[] containing the keys (environment variable names, YAML keys, or JSON keys) that this resolver is responsible for handling.
    2. resolveSecretValue(id: string): An asynchronous method that takes a secret identifier and returns the resolved string value (or undefined).

    This allows you to bridge configify with any external API, database, or secret management service.

    import { RemoteConfigurationResolverStrategy } from '@itgorillaz/configify';
    import { Axios } from 'axios';
    
    interface CustomSecret {
      value: string;
    }
    
    export class CustomConfigurationResolver
      implements RemoteConfigurationResolverStrategy
    {
      // Define which keys this resolver handles
      configurationKeys: readonly string[] = ['CUSTOM_SECRET', 'custom-secret'];
    
      constructor(
        private readonly url: string,
        private readonly axios: Axios,
      ) {}
    
      async resolveSecretValue(id: string): Promise<string | undefined> {
        const secretId = `${this.url}/${id}`;
        const result = await this.axios.get<CustomSecret>(secretId);
        return result.data.value;
      }
    }