@juicyllama/nestjs-config-loader
v1.4.3
Published
A Nestjs config loader with support for AWS Secrets Manager and environment variables
Readme
NestJS Configuration Loader
A powerful and flexible NestJS configuration loader with support for AWS Secrets Manager and environment variables. This library provides a decorator-based approach to configuration management with automatic validation, type conversion, and caching capabilities.
Features
- 🔐 AWS Secrets Manager Integration - Load sensitive configuration from AWS Secrets Manager
- 🌍 Environment Variables Support - Traditional environment variable loading with prefixes
- 🎯 Decorator-Based Configuration - Clean, declarative configuration classes
- ✅ Automatic Validation - Built-in validation using class-validator
- 🔄 Type Transformation - Automatic type conversion using class-transformer
- 💾 Global Caching - Efficient caching to avoid redundant AWS API calls
- 🔧 Flexible Configuration - Support for default values, optional fields, and custom key mappings
- 🏗️ Dependency Injection - Full NestJS DI container integration
Installation
npm install @juicyllama/nestjs-config-loaderPeer Dependencies
Make sure you have the following peer dependencies installed:
npm install @nestjs/common class-transformer class-validatorQuick Start
1. Create Configuration Classes
import {IsString, IsNumber, IsNotEmpty} from 'class-validator';
import {Transform} from 'class-transformer';
import {AwsSecretClass, AwsSecretKey, EnvVar} from '@juicyllama/nestjs-config-loader';
// Database configuration with AWS Secrets Manager
@AwsSecretClass({
secretId: 'prod/database/credentials',
region: 'us-east-1'
})
export class DatabaseConfig {
@AwsSecretKey()
@IsString()
@IsNotEmpty()
host: string;
@AwsSecretKey({key: 'db_port'})
@Transform(({value}) => parseInt(value, 10))
@IsNumber()
port: number;
@AwsSecretKey()
@IsString()
@IsNotEmpty()
username: string;
@AwsSecretKey()
@IsString()
@IsNotEmpty()
password: string;
@EnvVar({key: 'DB_SSL_ENABLED', defaultValue: 'false'})
@Transform(({value}) => value === 'true')
sslEnabled: boolean;
}
// Application configuration with environment variables
export class AppConfig {
@EnvVar({key: 'NODE_ENV', defaultValue: 'development'})
@IsString()
environment: string;
@EnvVar({key: 'PORT', defaultValue: '3000'})
@Transform(({value}) => parseInt(value, 10))
@IsNumber()
port: number;
}2. Register the Module
import {Module} from '@nestjs/common';
import {ConfigurationLoaderModule} from '@juicyllama/nestjs-config-loader';
import {DatabaseConfig, AppConfig} from './config';
@Module({
imports: [
ConfigurationLoaderModule.register({
isGlobal: true | false,
cache: true,
awsRegion: 'us-east-1',
configs: [DatabaseConfig, AppConfig]
})
]
})
export class AppModule {
}3. Use Configuration in Services
import {Injectable} from '@nestjs/common';
import {DatabaseConfig, AppConfig} from './config';
@Injectable()
export class MyService {
constructor(
private readonly databaseConfig: DatabaseConfig,
private readonly appConfig: AppConfig
) {
}
async connect() {
console.log(`Connecting to ${this.databaseConfig.host}:${this.databaseConfig.port}`);
console.log(`Running on port ${this.appConfig.port} in ${this.appConfig.environment} mode`);
}
}Decorators Reference
Class-Level Decorators
@AwsSecretClass(options: AwsSecretsConfigOptions)
Configures a class to load properties from AWS Secrets Manager.
Options:
secretId: string- The AWS Secrets Manager secret ID/name (required)region?: string- AWS region (optional, falls back to module configuration)required?: boolean- Whether this secret is required (default: true)
@AwsSecretClass({
secretId: 'prod/api/credentials',
region: 'us-west-2',
required: false
})
export class ApiConfig {
// ... properties
}Property-Level Decorators
@AwsSecretKey(options?: AwsSecretOptions)
Marks a property to be loaded from AWS Secrets Manager. Must be used within a class decorated with @AwsSecretClass.
Options:
key?: string- The key within the secret (defaults to property name)required?: boolean- Whether this property is required (default: true)defaultValue?: unknown- Default value if the secret key is not found
@AwsSecretKey() // Uses property name as key
apiKey
:
string;
@AwsSecretKey({key: 'webhook_secret'}) // Custom key name
webhookSecret
:
string;
@AwsSecretKey({
required: false,
defaultValue: 'default-value'
})
optionalKey
:
string;@EnvVar(keyOrOptions: string | EnvVarOptions)
Loads a property from environment variables.
Options (when using object form):
key: string- Environment variable name (required)required?: boolean- Whether this variable is required (default: true)defaultValue?: any- Default value if environment variable is not set
@EnvVar('DATABASE_URL') // Simple string form
databaseUrl
:
string;
@EnvVar({
key: 'PORT',
defaultValue: '3000'
})
port
:
string;
@EnvVar({
key: 'DEBUG_MODE',
required: false,
defaultValue: false
})
debugMode
:
boolean;Module Configuration
Synchronous Registration
ConfigurationLoaderModule.register({
isGlobal: false, // Make module globally available
cache: true, // Enable AWS secrets caching
awsRegion: 'us-east-1', // Default AWS region
awsSecretsEndpoint: 'https://secretsmanager.us-east-1.amazonaws.com', // Custom endpoint usefully for local development
envPrefix: 'MYAPP', // Prefix for environment variables - optional
envFilePath: ['.env'], // Custom .env file paths
envOverrideSecrets: true, // Whether env vars override secrets
configs: [DatabaseConfig, AppConfig] // Configuration classes
})Asynchronous Registration
ConfigurationLoaderModule.registerAsync({
isGlobal: false,
configs: [DatabaseConfig, AppConfig],
useFactory: async (configService: ConfigService) => ({
cache: true,
awsRegion: configService.get('AWS_REGION', 'us-east-1'),
envPrefix: configService.get('ENV_PREFIX')
}),
inject: [ConfigService]
})Configuration Options
| Option | Type | Default | Description |
|----------------------|----------------------|------------|----------------------------------------------------|
| isGlobal | boolean | false | Make the module globally available |
| cache | boolean | true | Enable caching for AWS secrets |
| awsRegion | string | - | Default AWS region for secrets |
| awsSecretsEndpoint | string | - | Custom AWS Secrets Manager endpoint |
| envPrefix | string | - | Prefix to add to all environment variable names |
| envFilePath | string[] | ['.env'] | Paths to .env files to load |
| envOverrideSecrets | boolean | true | Whether environment variables override AWS secrets |
| configs | ClassConstructor[] | [] | Configuration classes to register |
Advanced Usage
Mixing Environment Variables and AWS Secrets
You can combine both approaches in a single configuration class:
@AwsSecretClass({
secretId: 'prod/database/credentials'
})
export class DatabaseConfig {
// From AWS Secrets Manager
@AwsSecretKey()
@IsString()
password: string;
// From environment variables (takes precedence by default)
@EnvVar({key: 'DB_HOST', defaultValue: 'localhost'})
@IsString()
host: string;
// Optional AWS secret with fallback
@AwsSecretKey({
required: false,
defaultValue: 'myapp'
})
@IsString()
database: string;
}Custom Validation and Transformation
Leverage class-validator and class-transformer for robust configuration:
export class ServerConfig {
@EnvVar({key: 'PORT', defaultValue: '3000'})
@Transform(({value}) => parseInt(value, 10))
@IsNumber()
@Min(1)
@Max(65535)
port: number;
@EnvVar({key: 'ALLOWED_HOSTS'})
@Transform(({value}) => value.split(','))
@IsArray()
@IsString({each: true})
allowedHosts: string[];
@EnvVar({key: 'RATE_LIMIT', defaultValue: '100'})
@Transform(({value}) => parseInt(value, 10))
@IsNumber()
@Min(1)
rateLimit: number;
}Optional Configuration Classes
Handle optional secrets gracefully:
@AwsSecretClass({
secretId: 'optional/third-party/keys',
required: false // Won't fail if secret doesn't exist
})
export class ThirdPartyConfig {
@AwsSecretKey({
required: false,
defaultValue: null
})
stripeKey?: string;
@AwsSecretKey({
required: false,
defaultValue: null
})
twilioKey?: string;
}Error Handling
The library provides detailed error messages for common issues:
Missing Required Configuration
Configuration validation failed for DatabaseConfig:
- host: host should not be empty
- port: port must be a numberMissing AWS Secrets
Required secret key 'password' not found in secret 'prod/database/credentials'Missing Environment Variables
Required environment variable 'DATABASE_URL' is not definedBest Practices
- Use TypeScript: Leverage strong typing for better development experience
- Validate Everything: Use class-validator decorators for robust validation
- Group Related Config: Create separate classes for different concerns (database, cache, etc.)
- Use Default Values: Provide sensible defaults for non-critical configuration
- Environment Precedence: Let environment variables override secrets for flexibility
- Cache Secrets: Enable caching to improve performance and reduce AWS API calls
AWS Setup
Ensure your application has the necessary AWS permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": [
"arn:aws:secretsmanager:region:account:secret:your-secret-name-*"
]
}
]
}License
ISC
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
