@itgorillaz/configify
NestJS config on steroids
configify is a NestJS configuration module that simplifies loading, validating, and injecting configuration from environment variables, config files, and remote secret managers.
npm install --save @itgorillaz/configifyImport ConfigifyModule into your root module using forRootAsync():
import { ConfigifyModule } from '@itgorillaz/configify';
@Module({
imports: [ConfigifyModule.forRootAsync()],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}TypeScript strict mode: If you have
"strict": truein yourtsconfig.jsonand you are not using constructor configuration injection, you must also set"strictPropertyInitialization": false. This is required because configify initializes configuration properties at runtime.{ "compilerOptions": { "strict": true, "strictPropertyInitialization": false } }
By default, configify looks for the following files in the root of your project:
my-app/
├── .env
├── application.yml
└── application.json
All three formats are supported and can be used together. You can also customize the file paths.
Decorate any class with @Configuration() and configify will automatically discover it, populate its properties, and make the instance available for dependency injection throughout your application.
# .env
APPLICATION_CLIENT_ID=ABC
APPLICATION_CLIENT_TOKEN=TESTimport { Configuration, Value } from '@itgorillaz/configify';
@Configuration()
export class ApplicationClientConfig {
@Value('APPLICATION_CLIENT_ID')
appClientId: string;
@Value('APPLICATION_CLIENT_TOKEN')
appClientToken: string;
}# application.yml
database:
host: localhost
port: 3306
username: test
password: test
metadata: |
{
"label": "staging"
}import { Configuration, Value } from '@itgorillaz/configify';
interface DatabaseMetadata {
label: string;
}
@Configuration()
export class DatabaseConfiguration {
@Value('database.host')
host: string;
@Value('database.port', { parse: parseInt })
port: number;
@Value('database.metadata', { parse: JSON.parse })
metadata: DatabaseMetadata;
}A single config file can be split across multiple configuration classes to keep concerns separated:
# .env
DATABASE_HOST=localhost
DATABASE_USER=test
DATABASE_PASSWORD=test
OKTA_API_TOKEN=test
OKTA_CLIENT_ID=test@Configuration()
export class DatabaseConfiguration {
@Value('DATABASE_HOST')
host: string;
@Value('DATABASE_USER')
user: string;
@Value('DATABASE_PASSWORD')
password: string;
}
@Configuration()
export class OktaConfiguration {
@Value('OKTA_API_TOKEN')
apiToken: string;
@Value('OKTA_CLIENT_ID')
clientId: string;
}Decorate your configuration class with @RequiredArgsConstructor() and configify will automatically create the instance of the configuration class passing the configuration values as an object to the constructor.
# .env
DATABASE_HOST=localhost
DATABASE_USER=test
DATABASE_PASSWORD=test@Configuration()
@RequiredArgsConstructor()
export class DatabaseConfiguration {
@Value('DATABASE_HOST')
host: string;
@Value('DATABASE_USER')
user: string;
@Value('DATABASE_PASSWORD')
password: string;
constructor(config: Required<DatabaseConfiguration>) {
this.host = config.host;
this.user = config.user;
this.password = config.password;
}
}All @Configuration() classes are registered globally. Inject them into any provider via the constructor:
import { Injectable, Logger } from '@nestjs/common';
import { DatabaseConfiguration } from './database.configuration';
@Injectable()
export class AppService {
private readonly logger = new Logger(AppService.name);
constructor(private readonly dbConfig: DatabaseConfiguration) {
this.logger.log(
`Connecting to database at ${dbConfig.host}:${dbConfig.port}`,
);
}
}You can provide fallback values in two ways:
Option 1 — Variable expansion syntax (in the config file):
# .env
APP_CLIENT_ID=${NON_EXISTING_VAR:-DEFAULT_ID} # resolves to DEFAULT_IDOption 2 — @Value() decorator option:
@Configuration()
export class DatabaseConfiguration {
@Value('DB_HOST', { default: 'localhost' })
host: string;
@Value('DB_PORT', { parse: parseInt, default: 3306 })
port: number;
}configify supports variable expansion in config files, including cross-variable references and default fallbacks:
# .env
MY_SECRET=TEST
MY_API_KEY=${MY_SECRET} # resolves to: TEST
APP_CLIENT_ID=${NON_EXISTING:-DEFAULT_ID} # resolves to: DEFAULT_IDUse the parse option to transform raw string values into the types your application needs:
# application.yml
db-json-config: |
{
"host": "localhost",
"user": "test",
"password": "test"
}interface DbConfig {
host: string;
user: string;
password: string;
}
@Configuration()
export class DatabaseConfiguration {
@Value('db-json-config', { parse: JSON.parse })
dbConfig: DbConfig;
}The parse option accepts any function with the signature (value: string) => T, so you can use built-ins like parseInt, JSON.parse, or your own custom parser.
Use class-validator decorators alongside @Value() to validate configuration at startup — before your application begins serving requests:
npm install --save class-validatorimport { 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;
@IsUrl()
@Value('WEBHOOK_URL')
webhookUrl: string;
}If validation fails, the application will throw an error on startup with a clear message describing which values are missing or invalid.
configify has built-in support for resolving secrets from external providers:
| Provider | Notes |
|---|---|
| AWS Secrets Manager | |
| AWS Parameter Store | |
| Azure Key Vault | |
| Bitwarden Secrets Manager | |
| Google Cloud Secret Manager | |
| Custom resolver | Implement your own ConfigurationResolver |
See the examples directory for provider-specific setup instructions.
Pass an options object to forRootAsync() to customize module behavior:
ConfigifyModule.forRootAsync({
// Ignore all config files (.env, application.yml, application.json)
// Default: false
ignoreConfigFile: false,
// Ignore environment variables
// Default: false
ignoreEnvVars: false,
// Path(s) to configuration files (overrides default discovery)
configFilePath: './config/app.yml',
// or multiple files:
// configFilePath: ['./config/database.yml', './config/app.env'],
// Enable/disable variable expansion
// Default: true
expandConfig: true,
// Custom secrets resolver strategies
secretsResolverStrategies: [],
});This code is licensed under the MIT License.
All files located in the node_modules and external directories are externally maintained libraries used by this software which have their own licenses; we recommend you read them, as their terms may differ from the terms in the MIT License.