Skip to content

platform-configuration

Type-safe configuration management for NestJS applications. Provides compile-time safe config keys, a strategy-based resolution pattern (environment variables, .env files, or custom sources), a global NestJS module, and a central registry for config discovery and validation.

Note: This package was extracted from platform-core. Re-exports are still available from platform-core for backward compatibility.

Package: @breadstone/archipel-platform-configuration

npm install @breadstone/archipel-platform-configuration

Quick Start

typescript
import { Module } from '@nestjs/common';
import { ConfigModule, EnvironmentConfigStrategy } from '@breadstone/archipel-platform-configuration';

@Module({
  imports: [
    ConfigModule.forRoot({
      strategyFactory: () => new EnvironmentConfigStrategy(),
    }),
  ],
})
export class AppModule {}

Config Keys

Type-safe config keys encode both the key name and the expected value type at compile time. This eliminates scattered process.env access and provides IDE autocomplete, "find usages", and refactoring support.

Defining Config Keys

typescript
import { createConfigKey } from '@breadstone/archipel-platform-configuration';

export const DATABASE_URL = createConfigKey<string>('DATABASE_URL');
export const CACHE_TTL = createConfigKey<number>('CACHE_TTL');
export const FEATURE_ENABLED = createConfigKey<boolean>('FEATURE_ENABLED');

IConfigKey Interface

typescript
interface IConfigKey<TType = unknown> {
  readonly _type?: TType; // Phantom type — compile-time only
  readonly key: string; // The unique string identifier
}

ConfigModule

forRoot — Application Root

Call once in the root module to bootstrap the global ConfigService.

typescript
ConfigModule.forRoot({
  strategyFactory: () => new EnvironmentConfigStrategy(),
  statics: { APP_VERSION: '1.0.0' }, // optional static overrides
});

register — Library Modules

Call in each library module to declare configuration dependencies.

typescript
import { createConfigKey, type IConfigRegistryEntry } from '@breadstone/archipel-platform-configuration';

const SMTP_HOST = createConfigKey<string>('SMTP_HOST');
const SMTP_PORT = createConfigKey<number>('SMTP_PORT');

export const MAIL_CONFIG_ENTRIES: ReadonlyArray<IConfigRegistryEntry> = [
  { key: SMTP_HOST, module: 'mailing', required: true, description: 'SMTP server hostname' },
  { key: SMTP_PORT, module: 'mailing', required: false, defaultValue: 587, description: 'SMTP port' },
];

@Module({
  imports: [ConfigModule.register('mailing', MAIL_CONFIG_ENTRIES)],
})
export class MailModule {}

IConfigModuleOptions

PropertyTypeDescription
strategyFactory() => ConfigStrategyBaseFactory that creates the resolution strategy for config values
staticsRecord<string, string>Optional key-value overrides injected at startup

ConfigService

Injected globally after ConfigModule.forRoot(). Automatically parses raw string values into numbers, booleans, and null.

typescript
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@breadstone/archipel-platform-configuration';

@Injectable()
export class SmtpClient {
  private readonly _host: string;
  private readonly _port: number;

  constructor(configService: ConfigService) {
    this._host = configService.get<string>('SMTP_HOST');
    this._port = configService.tryGet<number>('SMTP_PORT', 587);
  }
}
MethodDescription
get<T>(key)Returns the parsed value. Throws ConfigKeyNotFoundError if missing.
tryGet<T>(key, fallback)Returns the parsed value, or fallback if the key is not set.
has(key)Returns true if the key exists in the strategy.

ConfigRegistry

Static, global registry that collects all configuration keys registered by platform libraries. Enables a central overview of every environment variable the application depends on, grouped by source module.

typescript
import { ConfigRegistry } from '@breadstone/archipel-platform-configuration';

// All registered entries
const all = ConfigRegistry.getAll();

// Grouped by module
const grouped = ConfigRegistry.getByModule();
// Map { 'mailing' => [...], 'authentication' => [...] }

Strategies

EnvironmentConfigStrategy

Resolves values from process.env.

typescript
import { EnvironmentConfigStrategy } from '@breadstone/archipel-platform-configuration';

ConfigModule.forRoot({
  strategyFactory: () => new EnvironmentConfigStrategy(),
});

FileConfigStrategy

Resolves values from a .env file on disk using dotenv.

typescript
import { FileConfigStrategy } from '@breadstone/archipel-platform-configuration';

ConfigModule.forRoot({
  strategyFactory: () => new FileConfigStrategy('.env'),
});

Custom Strategies

Extend ConfigStrategyBase to implement custom resolution (e.g. Azure App Configuration, AWS Parameter Store).

typescript
import { ConfigStrategyBase } from '@breadstone/archipel-platform-configuration';

export class AzureAppConfigStrategy extends ConfigStrategyBase {
  public initialize(): void {
    // Connect to Azure App Configuration
  }

  public resolve<T>(key: string): T {
    // Fetch from Azure
  }

  public tryResolve<T>(key: string, fallback: T): T {
    // Attempt fetch, return fallback on miss
  }

  public has(key: string): boolean {
    // Check existence
  }
}

Error Handling

ErrorDescription
ConfigKeyNotFoundErrorThrown when get() is called for a key that does not exist in the active strategy.

Module Summary

ExportTypeDescription
ConfigModuleNestJS ModuleGlobal configuration bootstrap and registration
ConfigServiceServiceType-safe config value access
ConfigRegistryStatic classCentral registry of all registered config entries
ConfigStrategyBaseAbstract classBase class for configuration resolution strategies
EnvironmentConfigStrategyStrategyResolves from process.env
FileConfigStrategyStrategyResolves from .env files via dotenv
createConfigKey<T>()FactoryCreates a typed config key
IConfigKey<T>InterfaceTyped config key contract
IConfigRegistryEntry<T>InterfaceRegistry entry with module, required, default
IConfigModuleOptionsInterfaceOptions for ConfigModule.forRoot()
ConfigKeyNotFoundErrorErrorThrown on missing required config keys

Released under the MIT License.