Skip to content

4. Basic Usage Guide

Michael De Soto edited this page May 7, 2025 · 1 revision

This section guides you through the fundamental steps to integrate NestJS Secrets into your application, load configuration files (with and without secrets), and access your configuration values.

1. Loading Configuration Files

NestJS Secrets loads configuration from YAML or JSON files. You can specify multiple files; they will be merged, with values from later files taking precedence over earlier ones. This is useful for environment-specific overrides.

First, create your configuration file(s). For example, a settings.yaml:

# settings.yaml
application:
    name: My Awesome App
    port: 3000

database:
    host: db.example.com
    port: 5432
    username: db-user
    # This value will be fetched from a secret manager
    password: 'arn:aws:ssm:us-east-1:123456789012:parameter/myapplication/dev/api_key' # Example for AWS Parameter Store

featureFlags:
    newDashboard: true

In this example, arn:aws:ssm:us-east-1:123456789012:parameter/myapplication/dev/api_key is a reference to a secret. NestJS Secrets will automatically resolve this to its actual value if a suitable secret provider is configured. The specific reference format depends on the cloud provider (details in Section 4: Using with Cloud Providers).

You can also have environment-specific files, like settings.local.yaml, to override values for local development:

# settings.local.yaml
application:
    port: 3001 # Override port for local development

database:
    # Override password for local development, perhaps a non-secret or different reference
    password: 'localdevpassword'

2. Integrating with SecretsModule

Import and configure SecretsModule in your root AppModule. The forRoot() method initializes the configuration system, loading your files and setting up secret resolution if a provider client is supplied.

Loading Files Without Secret Resolution:

If you only need to load and merge local configuration files (YAML/JSON) without resolving any external secrets, you can set up the module like this:

// app.module.ts
import {Module} from '@nestjs/common';
import {SecretsModule} from '@floracodex/nestjs-secrets';

@Module({
    imports: [
        SecretsModule.forRoot({
            // Optional: specify a subdirectory for your config files
            // root: './config',

            files: ['settings.yaml', 'settings.local.yaml'],

            // Makes ConfigService available globally
            isGlobal: true,

            // Enables caching of configuration values
            cache: true
        })

        // IMPORTANT: Do NOT add ConfigModule.forRoot() here if using SecretsModule.forRoot()
    ]
})
export class AppModule {
}

Enabling Secret Resolution from Cloud Providers:

To resolve secret references (like 'arn:aws:ssm:us-east-1:123456789012:parameter/myapplication/api_key') from cloud providers, you need to provide an initialized SDK client for that provider.

  • Auto-detection (Recommended for built-in providers):

    NestJS Secrets can often automatically determine which secret provider to use based on the SDK client you provide.

    Example for Google Cloud Secret Manager:

    // app.module.ts
    import {Module} from '@nestjs/common';
    import {SecretsModule} from '@floracodex/nestjs-secrets';
    import {SecretManagerServiceClient} from '@google-cloud/secret-manager';
    
    @Module({
        imports: [
            SecretsModule.forRoot({
                files: ['settings.yaml', 'settings.local.yaml'],
                isGlobal: true,
                cache: true,
    
                // Provide the SDK client for Google Cloud Secret Manager
                // The `provider: 'GoogleSecretManagerProvider'` will be inferred
                client: new SecretManagerServiceClient()
            })
    
            // Remeber, do NOT add ConfigModule.forRoot() here
        ]
    })
    export class AppModule {
    }
  • Explicit Provider Declaration:

    You can also explicitly declare the provider name. This can be useful for clarity or if there's any ambiguity.

    // app.module.ts
    import { Module } from '@nestjs/common';
    import { SecretsModule } from '@floracodex/nestjs-secrets';
    import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
    
    @Module({
      imports: [
        SecretsModule.forRoot({
          files: ['settings.yaml', 'settings.local.yaml'],
          isGlobal: true,
          cache: true,
    
          provider: 'GoogleSecretManagerProvider', // Explicitly name the provider
          client: new SecretManagerServiceClient()
        })
      ]
    })
    export class AppModule {}

Important Note on ConfigModule.forRoot():

When you use SecretsModule.forRoot(), it handles the necessary setup of NestJS's configuration system internally, including applying options like isGlobal, cache, ignoreEnvFile, etc., that you might recognize from @nestjs/config. Therefore, you should not also call ConfigModule.forRoot() from @nestjs/config in the same module imports (e.g., your root AppModule). Doing so might lead to conflicts or unexpected behavior with how ConfigService is provided and configured.

If you opt for an advanced setup where you manually use SecretsLoaderService with a factory pattern (covered in the Advanced Usage section), you will then be responsible for setting up ConfigModule.forRoot() yourself.

Explanation of Core Configuration Options (for SecretsModule.forRoot()):

  • files: An array of configuration file paths to load (e.g., ['settings.yaml', 'settings.dev.yaml']). Later files override earlier ones.
  • root (optional): The base directory for configuration files. Defaults to the project root.
  • isGlobal (optional, from @nestjs/config): If true, ConfigService is available globally without needing to import SecretsModule in other modules.
  • cache (optional, from @nestjs/config): If true, resolved configuration (including secrets) is cached for performance.
  • client (optional): An initialized SDK client instance for your cloud secret manager (e.g., new SecretManagerServiceClient() for Google Cloud, new SSMClient() for AWS SSM). If provided, and provider is not a custom instance, the library will attempt to use this client to resolve secrets, often auto-detecting the provider type.
  • provider (optional):
    • Can be a string identifier for a built-in provider (e.g., 'GoogleSecretManagerProvider', 'AwsParameterStoreProvider'). Use with a client.
    • Can be an instance of a custom secret provider (see Advanced Usage), in which case client is typically omitted.

If neither a client (for a built-in provider) nor a custom provider instance is supplied, NestJS Secrets will still load and merge your configuration files but will not attempt to resolve any secret reference strings. Specific examples for each supported cloud provider are below.

3. Accessing Configuration in Your Services

Once SecretsModule is configured (and typically made global via isGlobal: true), you can inject and use the standard NestJS ConfigService to access your configuration values anywhere in your application. Resolved secret values are available just like any other configuration key.

// any.service.ts
import {Injectable} from '@nestjs/common';
import {ConfigService} from '@nestjs/config';

@Injectable()
export class AnyService {
    private dbHost: string;
    private dbPassword_resolved: string; // Will contain the actual password if resolved

    constructor(private configService: ConfigService) {
        // Access simple configuration values
        const appName = this.configService.get<string>('application.name');
        this.dbHost = this.configService.get<string>('database.host');

        // Access a value that was potentially resolved from a secret manager
        this.dbPassword_resolved = this.configService.get<string>('database.password');

        console.log('Application Name:', appName);
        console.log('Database Host:', this.dbHost);
        // console.log('Database Password (Resolved):', this.dbPassword_resolved);
        // Note: Be cautious logging sensitive values, even in development!
    }

    getDatabaseConfig() {
        return {
            host: this.configService.get('database.host'),
            port: this.configService.get<number>('database.port'),
            username: this.configService.get('database.username'),
            password: this.configService.get('database.password') // Already resolved!
        };
    }
}

This approach leverages NestJS's built-in dependency injection and configuration handling, allowing NestJS Secrets to seamlessly provide resolved secrets without changing how you typically access configuration.

Clone this wiki locally