Configuration System

Hierarchical configuration management with environment overrides, caching, and type-safe access.

JSON ConfigEnvironmentDot NotationCachingType Safety

Quick Start

Basic Usage

dart
// Basic configuration access
final appName = Khadem.config.get<String>('app.name');
final port = Khadem.config.get<int>('app.port', 3000);
final debug = Khadem.config.get<bool>('app.debug', false);

// Environment variables
final apiKey = Khadem.env.get('API_KEY');
final dbUrl = Khadem.env.getOrDefault('DATABASE_URL', 'sqlite:memory:');

💡 Note: Configuration is automatically loaded via service provider

⚡ Tip: Use dot notation like 'app.database.host' for nested access

Configuration Structure

Directory Layout

bash
config/
├── app.json              # Base application config
├── database.json         # Base database config
├── cache.json            # Base cache config
└── development/          # Environment-specific overrides
    ├── app.json         # Development app overrides
    ├── database.json    # Development database overrides
    └── cache.json       # Development cache overrides
Base Configuration

Default values for all environments

Environment Overrides

Environment-specific values that override base config

json
{
  "name": "KhademApp",
  "env": "production",
  "port": 8080,
  "debug": false,
  "database": {
    "host": "localhost",
    "port": 5432,
    "database": "khadem"
  }
}
json
{
  "env": "development",
  "port": 3000,
  "debug": true,
  "database": {
    "host": "localhost",
    "port": 5432,
    "database": "khadem_dev"
  }
}

Core Operations

dart
// Get values with type safety
final appName = Khadem.config.get<String>('app.name');
final port = Khadem.config.get<int>('app.port');
final debug = Khadem.config.get<bool>('app.debug');

// Get with defaults
final timeout = Khadem.config.get<int>('app.timeout', 30);
final host = Khadem.config.get<String>('database.host', 'localhost');

// Check existence
if (Khadem.config.has('database.url')) {
  final url = Khadem.config.get<String>('database.url');
}
dart
// Set runtime values
Khadem.config.set('app.maintenance', true);
Khadem.config.set('cache.ttl', 3600);

// Nested object access
final dbHost = Khadem.config.get<String>('database.host');
final dbPort = Khadem.config.get<int>('database.port');

// Get entire sections
final dbConfig = Khadem.config.section('database');
final allConfig = Khadem.config.all();

// Reload configuration
Khadem.config.reload();

⚠️ Important Notes

  • • Configuration files are cached with TTL for performance
  • • Environment overrides are merged with base configuration
  • • Use reload() to refresh configuration from disk
  • • Runtime changes don't persist to files

Environment Variables

dart
// Environment variable access
final apiKey = Khadem.env.get('API_KEY');
final dbUrl = Khadem.env.getOrDefault('DATABASE_URL', 'sqlite:memory:');

// Type-safe environment access
final port = Khadem.env.getInt('PORT', defaultValue: 3000);
final debug = Khadem.env.getBool('DEBUG', defaultValue: false);
final rate = Khadem.env.getDouble('EXCHANGE_RATE', defaultValue: 1.0);

// List values
final hosts = Khadem.env.getList('ALLOWED_HOSTS', separator: ',');
final paths = Khadem.env.getList('PATHS', separator: ';');

// Set environment values
Khadem.env.set('TEMP_VAR', 'temporary_value');

.env File Support

env
# Application settings
APP_NAME=MyApp
APP_VERSION=1.0.0
DEBUG=true
PORT=3000

# Database configuration
DATABASE_URL="postgresql://user:pass@localhost:5432/db"

# Export statements supported
export REDIS_URL=redis://localhost:6379

# Variable substitution
API_BASE_URL=http://localhost:$PORT
FULL_NAME="${APP_NAME} v${APP_VERSION}"

# List values
ALLOWED_HOSTS=localhost,127.0.0.1,::1

Type-Safe Access

dart
// String values
final name = Khadem.config.get<String>('app.name');
final url = Khadem.config.get<String>('database.url', 'sqlite:memory:');

// Numeric values
final port = Khadem.config.get<int>('app.port', 3000);
final timeout = Khadem.config.get<double>('app.timeout', 30.0);

// Boolean values
final debug = Khadem.config.get<bool>('app.debug', false);
final enabled = Khadem.config.get<bool>('feature.enabled', true);

// List values (from environment)
final hosts = Khadem.env.getList('ALLOWED_HOSTS');
final paths = Khadem.env.getList('SEARCH_PATHS', separator: ';');

// Environment with type conversion
final maxConn = Khadem.env.getInt('MAX_CONNECTIONS', defaultValue: 10);
final rateLimit = Khadem.env.getDouble('RATE_LIMIT', defaultValue: 100.0);

Supported Types

  • String - Text values
  • int - Integer numbers
  • double - Floating point numbers
  • bool - Boolean values (true/false)
  • List<String> - String arrays
  • Map<String, dynamic> - Nested objects

Runtime Configuration

dart
// Runtime configuration changes
Khadem.config.set('app.maintenance', true);
Khadem.config.set('cache.enabled', false);
Khadem.config.set('database.connections.max', 50);

// Dynamic feature flags
class FeatureFlags {
  static bool isEnabled(String feature) {
    return Khadem.config.get<bool>('features.$feature', false);
  }

  static void enable(String feature) {
    Khadem.config.set('features.$feature', true);
  }

  static void disable(String feature) {
    Khadem.config.set('features.$feature', false);
  }
}

// Usage
if (FeatureFlags.isEnabled('new_ui')) {
  // Show new UI
}
FeatureFlags.enable('beta_feature');

Use Cases

  • • Dynamic feature flags
  • • Runtime service configuration
  • • Testing environment setup
  • • Administrative overrides

Configuration Sections

dart
// Get entire configuration sections
final appConfig = Khadem.config.section('app');
final dbConfig = Khadem.config.section('database');
final cacheConfig = Khadem.config.section('cache');

// Work with section data
if (appConfig != null) {
  print('App Name: ${appConfig['name']}');
  print('Debug Mode: ${appConfig['debug']}');
}

// Get all configuration
final allConfig = Khadem.config.all();
for (final section in allConfig.keys) {
  print('Section: $section');
  print('Values: ${allConfig[section]}');
}

// Check configuration existence
final hasDatabase = Khadem.config.has('database');
final hasCache = Khadem.config.has('cache.host');
final hasFeature = Khadem.config.has('features.new_ui');

Section Operations

  • section('name') - Get entire section as map
  • all() - Get all configuration data
  • has('key') - Check if key exists
  • reload() - Refresh from disk

Registry-Based Loading

dart
// Load configuration from code
Khadem.loadConfigs({
  'app': {
    'name': 'MyApp',
    'version': '1.0.0',
    'debug': true
  },
  'database': {
    'host': 'localhost',
    'port': 5432,
    'database': 'myapp'
  },
  'cache': {
    'driver': 'memory',
    'ttl': 3600
  }
});

// Or load single sections
final customConfig = {
  'api': {
    'base_url': 'https://api.example.com',
    'timeout': 30,
    'retries': 3
  }
};
Khadem.config.loadFromRegistry(customConfig);

When to Use Registry

  • • Programmatic configuration setup
  • • Testing with mock configurations
  • • Dynamic configuration generation
  • • Configuration from external sources

Best Practices

✅ Do's

  • • Use descriptive, namespaced keys
  • • Provide sensible default values
  • • Use environment-specific overrides
  • • Validate configuration on startup
  • • Document configuration options
  • • Use type-safe access methods
  • • Cache expensive configuration lookups

❌ Don'ts

  • • Don't hardcode configuration values
  • • Don't access config in performance-critical loops
  • • Don't store sensitive data in config files
  • • Don't modify config files at runtime
  • • Don't use config for frequently changing data
  • • Don't skip validation of required config
  • • Don't use generic types without defaults

Common Patterns

Configuration Validation

Validate required configuration on startup

dart
// Configuration validation on startup
class ConfigValidator {
  static void validate() {
    final missing = <String>[];

    // Required configuration
    if (!Khadem.config.has('app.name')) {
      missing.add('app.name');
    }
    if (!Khadem.config.has('database.host')) {
      missing.add('database.host');
    }

    // Required environment variables
    final missingEnv = Khadem.env.validateRequired([
      'DATABASE_URL',
      'API_KEY',
      'JWT_SECRET'
    ]);

    missing.addAll(missingEnv);

    if (missing.isNotEmpty) {
      throw Exception('Missing required configuration: $missing');
    }

    // Validate value ranges
    final port = Khadem.config.get<int>('app.port', 3000);
    if (port < 1000 || port > 65535) {
      throw Exception('Invalid port number: $port');
    }
  }
}

// Call during app startup
void main() {
  ConfigValidator.validate();
  // Continue with app initialization
}

Feature Flags

Runtime feature toggles

dart
// Feature flags implementation
class FeatureManager {
  static const String _prefix = 'features';

  static bool isEnabled(String feature) {
    return Khadem.config.get<bool>('$_prefix.$feature', false);
  }

  static void enable(String feature) {
    Khadem.config.set('$_prefix.$feature', true);
    _logFeatureChange(feature, true);
  }

  static void disable(String feature) {
    Khadem.config.set('$_prefix.$feature', false);
    _logFeatureChange(feature, false);
  }

  static List<String> getEnabledFeatures() {
    final features = Khadem.config.section(_prefix);
    if (features == null) return [];

    return features.entries
        .where((entry) => entry.value == true)
        .map((entry) => entry.key)
        .toList();
  }

  static void _logFeatureChange(String feature, bool enabled) {
    Khadem.logger.info(
      'Feature flag changed: $feature = $enabled'
    );
  }
}

// Usage throughout the app
if (FeatureManager.isEnabled('new_dashboard')) {
  return NewDashboard();
} else {
  return OldDashboard();
}

Environment-Specific Config

Different settings per environment

dart
// Environment-specific configuration
class EnvironmentConfig {
  static String get environment {
    return Khadem.config.get<String>('app.env', 'production');
  }

  static bool get isDevelopment {
    return environment == 'development';
  }

  static bool get isProduction {
    return environment == 'production';
  }

  static bool get isTesting {
    return environment == 'testing';
  }

  static String get databaseName {
    final baseName = Khadem.config.get<String>('database.name', 'app');
    return isTesting ? '${baseName}_test' : baseName;
  }

  static int get cacheTtl {
    return isDevelopment ? 60 : 3600; // 1 min dev, 1 hour prod
  }

  static bool get enableDebugLogging {
    return isDevelopment || Khadem.config.get<bool>('app.debug', false);
  }

  static String get logLevel {
    return isDevelopment ? 'DEBUG' : 'INFO';
  }
}

// Usage
if (EnvironmentConfig.isDevelopment) {
  // Development-specific code
}

final dbName = EnvironmentConfig.databaseName;
final cacheTtl = EnvironmentConfig.cacheTtl;

On this page