CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-ktor--ktor-server-core

Ktor Server Core library providing foundational infrastructure for building asynchronous web applications and REST APIs with Kotlin

Pending
Overview
Eval results
Files

configuration.mddocs/

Configuration System

Ktor's configuration system provides flexible application configuration management through multiple sources including files, environment variables, and programmatic configuration. The system supports hierarchical configuration with type-safe access and validation.

ApplicationConfig Interface

Core Configuration Interface

interface ApplicationConfig {
    fun property(path: String): ApplicationConfigValue
    fun propertyOrNull(path: String): ApplicationConfigValue?
    fun config(path: String): ApplicationConfig
    fun configList(path: String): List<ApplicationConfig>
    fun keys(): Set<String>
    fun toMap(): Map<String, Any?>
}

Configuration Value Interface

interface ApplicationConfigValue {
    val type: Type
    
    fun getString(): String
    fun getList(): List<String>
    fun getInt(): Int
    fun getLong(): Long
    fun getFloat(): Float 
    fun getDouble(): Double
    fun getBoolean(): Boolean
}

Basic Configuration Usage

// Access configuration properties
fun Application.loadConfig() {
    val config = environment.config
    
    // String properties
    val appName = config.property("app.name").getString()
    val version = config.propertyOrNull("app.version")?.getString() ?: "1.0.0"
    
    // Numeric properties  
    val port = config.property("server.port").getInt()
    val timeout = config.property("server.timeout").getLong()
    val rate = config.property("app.rate").getDouble()
    
    // Boolean properties
    val debugMode = config.property("app.debug").getBoolean()
    val enableLogging = config.propertyOrNull("logging.enabled")?.getBoolean() ?: true
    
    // List properties
    val allowedHosts = config.property("server.allowedHosts").getList()
    val modules = config.propertyOrNull("app.modules")?.getList() ?: emptyList()
}

Configuration Implementations

MapApplicationConfig

// Configuration backed by Map
class MapApplicationConfig(vararg values: Pair<String, String>) : ApplicationConfig {
    constructor(map: Map<String, String>)
    
    // Add or update configuration values
    fun put(key: String, value: String)
    fun putAll(values: Map<String, String>)
}

// Create programmatic configuration
val config = MapApplicationConfig(
    "app.name" to "MyKtorApp",
    "server.port" to "8080", 
    "server.host" to "0.0.0.0",
    "database.url" to "jdbc:h2:mem:test",
    "database.driver" to "org.h2.Driver",
    "logging.level" to "INFO"
)

// Use with application environment
val environment = applicationEnvironment {
    this.config = config
}

MergedApplicationConfig

// Configuration that merges multiple configs with precedence
class MergedApplicationConfig(
    vararg configs: ApplicationConfig
) : ApplicationConfig {
    // Later configs override earlier ones
}

// Example: Environment variables override file config
val fileConfig = HoconApplicationConfig(ConfigFactory.load())
val envConfig = MapApplicationConfig(System.getenv().mapKeys { "env.${it.key}" })
val merged = MergedApplicationConfig(fileConfig, envConfig)

HOCON Configuration Support

HoconApplicationConfig

// Configuration using Typesafe Config (HOCON format)
class HoconApplicationConfig(
    private val config: Config
) : ApplicationConfig {
    
    constructor(configPath: String) : this(ConfigFactory.load(configPath))
    
    // Access nested configuration
    override fun config(path: String): ApplicationConfig {
        return HoconApplicationConfig(config.getConfig(path))
    }
}

// Load HOCON configuration
val config = HoconApplicationConfig(ConfigFactory.load())

// application.conf example:
/*
app {
    name = "MyKtorApp"
    version = "1.0.0"
    debug = false
    
    server {
        port = 8080
        host = "0.0.0.0"
        ssl {
            enabled = false
            keyStore = "keystore.jks"
            keyAlias = "mykey" 
        }
    }
    
    database {
        url = "jdbc:postgresql://localhost:5432/mydb"
        driver = "org.postgresql.Driver"
        user = "dbuser"
        password = "dbpass"
        
        pool {
            maxSize = 20
            minIdle = 5
            maxLifetime = 1800000
        }
    }
    
    logging {
        level = "INFO"
        appenders = ["console", "file"]
        
        file {
            path = "logs/app.log" 
            maxSize = "10MB"
            maxHistory = 30
        }
    }
}
*/

Hierarchical Configuration Access

fun Application.configureFromHocon() {
    val config = environment.config
    
    // Access nested configurations
    val serverConfig = config.config("server")
    val port = serverConfig.property("port").getInt()
    val host = serverConfig.property("host").getString()
    
    val sslConfig = serverConfig.config("ssl")
    val sslEnabled = sslConfig.property("enabled").getBoolean()
    
    if (sslEnabled) {
        val keyStore = sslConfig.property("keyStore").getString()
        val keyAlias = sslConfig.property("keyAlias").getString()
        // Configure SSL
    }
    
    // Access configuration lists
    val dbConfigs = config.configList("databases")
    dbConfigs.forEach { dbConfig ->
        val url = dbConfig.property("url").getString()
        val driver = dbConfig.property("driver").getString()
        // Configure database connection
    }
}

Environment Variable Configuration

Environment Integration

// Create configuration from environment variables
fun createEnvironmentConfig(): ApplicationConfig {
    val envVars = System.getenv()
    val configMap = mutableMapOf<String, String>()
    
    // Map environment variables to config paths
    envVars.forEach { (key, value) ->
        when {
            key.startsWith("KTOR_") -> {
                val configKey = key.removePrefix("KTOR_")
                    .lowercase()
                    .replace('_', '.')
                configMap[configKey] = value
            }
        }
    }
    
    return MapApplicationConfig(configMap)
}

// Example environment variables:
// KTOR_SERVER_PORT=8080
// KTOR_SERVER_HOST=0.0.0.0  
// KTOR_DATABASE_URL=jdbc:postgresql://localhost/mydb
// KTOR_LOGGING_LEVEL=DEBUG

// Usage
val envConfig = createEnvironmentConfig()
val fileConfig = HoconApplicationConfig(ConfigFactory.load())
val config = MergedApplicationConfig(fileConfig, envConfig) // Env overrides file

System Properties Integration

// Create configuration from system properties
fun createSystemPropsConfig(): ApplicationConfig {
    val sysProps = System.getProperties()
    val configMap = mutableMapOf<String, String>()
    
    sysProps.forEach { key, value ->
        if (key.toString().startsWith("ktor.")) {
            configMap[key.toString()] = value.toString()
        }
    }
    
    return MapApplicationConfig(configMap)
}

// Usage with JVM arguments:
// java -Dktor.server.port=8080 -Dktor.debug=true MyApp

Configuration Loaders

ConfigLoaders Object

object ConfigLoaders {
    fun file(path: String): ApplicationConfig {
        return HoconApplicationConfig(ConfigFactory.parseFile(File(path)))
    }
    
    fun classpath(resource: String): ApplicationConfig {
        return HoconApplicationConfig(ConfigFactory.parseResources(resource))
    }
    
    fun properties(path: String): ApplicationConfig {
        val props = Properties()
        File(path).inputStream().use { props.load(it) }
        return MapApplicationConfig(props.toMap() as Map<String, String>)
    }
    
    fun environment(): ApplicationConfig {
        return createEnvironmentConfig()  
    }
    
    fun systemProperties(): ApplicationConfig {
        return createSystemPropsConfig()
    }
}

// Load configuration from multiple sources
val config = MergedApplicationConfig(
    ConfigLoaders.classpath("application.conf"),      // Default config
    ConfigLoaders.file("config/production.conf"),    // Environment-specific
    ConfigLoaders.environment(),                      // Environment variables
    ConfigLoaders.systemProperties()                  // JVM system properties
)

Configuration Decoder

MapConfigDecoder

// Decoder for map-based configurations
class MapConfigDecoder {
    fun decode(map: Map<String, Any?>): ApplicationConfig {
        return MapApplicationConfig().apply {
            map.forEach { (key, value) ->
                when (value) {
                    is String -> put(key, value)
                    is Number -> put(key, value.toString())
                    is Boolean -> put(key, value.toString()) 
                    is List<*> -> {
                        // Handle list values
                        value.forEachIndexed { index, item ->
                            put("$key.$index", item.toString())
                        }
                    }
                    is Map<*, *> -> {
                        // Handle nested maps
                        @Suppress("UNCHECKED_CAST")
                        val nested = value as Map<String, Any?>
                        nested.forEach { (nestedKey, nestedValue) ->
                            put("$key.$nestedKey", nestedValue.toString())
                        }
                    }
                }
            }
        }
    }
}

Deployment Configuration Extensions

Standard Deployment Properties

// Extension properties for common deployment settings
val ApplicationConfig.port: Int
    get() = propertyOrNull("ktor.deployment.port")?.getInt() ?: 8080

val ApplicationConfig.host: String  
    get() = propertyOrNull("ktor.deployment.host")?.getString() ?: "0.0.0.0"

val ApplicationConfig.sslPort: Int?
    get() = propertyOrNull("ktor.security.ssl.port")?.getInt()

val ApplicationConfig.developmentMode: Boolean
    get() = propertyOrNull("ktor.development")?.getBoolean() ?: false

val ApplicationConfig.rootPath: String
    get() = propertyOrNull("ktor.deployment.rootPath")?.getString() ?: ""

// Usage
fun Application.configureFromDeployment() {
    val config = environment.config
    
    log.info("Starting server on ${config.host}:${config.port}")
    log.info("Development mode: ${config.developmentMode}")
    log.info("Root path: ${config.rootPath}")
    
    config.sslPort?.let { sslPort ->
        log.info("SSL enabled on port $sslPort")
    }
}

Watch Paths Configuration

// Configure watch paths for development auto-reload
fun ApplicationEnvironmentBuilder.configureWatchPaths(config: ApplicationConfig) {
    config.propertyOrNull("ktor.deployment.watch")?.getList()?.forEach { path ->
        watchPaths.add(path)
    }
    
    // Or from individual watch entries
    config.configList("ktor.deployment.watch").forEach { watchConfig ->
        val path = watchConfig.property("path").getString()
        watchPaths.add(path)
    }
}

// application.conf example:
/*
ktor {
    deployment {
        watch = [
            "src/main/kotlin",
            "src/main/resources"
        ]
        
        # Alternative format
        watch = [
            { path = "src/main/kotlin" },
            { path = "src/main/resources" }
        ]
    }
}
*/

Type-Safe Configuration

Configuration Data Classes

// Define configuration data classes
data class ServerConfig(
    val port: Int,
    val host: String,
    val ssl: SslConfig?
)

data class SslConfig(
    val port: Int,
    val keyStore: String,
    val keyPassword: String,
    val keyAlias: String
)

data class DatabaseConfig(
    val url: String,
    val driver: String,
    val user: String,
    val password: String,
    val pool: PoolConfig
)

data class PoolConfig(
    val maxSize: Int,
    val minIdle: Int,
    val maxLifetime: Long
)

// Extension functions for type-safe access
fun ApplicationConfig.getServerConfig(): ServerConfig {
    val serverConfig = config("server")
    return ServerConfig(
        port = serverConfig.property("port").getInt(),
        host = serverConfig.property("host").getString(),
        ssl = serverConfig.propertyOrNull("ssl")?.let {
            val sslConfig = serverConfig.config("ssl")
            SslConfig(
                port = sslConfig.property("port").getInt(),
                keyStore = sslConfig.property("keyStore").getString(),
                keyPassword = sslConfig.property("keyPassword").getString(),
                keyAlias = sslConfig.property("keyAlias").getString()
            )
        }
    )
}

fun ApplicationConfig.getDatabaseConfig(): DatabaseConfig {
    val dbConfig = config("database")
    return DatabaseConfig(
        url = dbConfig.property("url").getString(),
        driver = dbConfig.property("driver").getString(), 
        user = dbConfig.property("user").getString(),
        password = dbConfig.property("password").getString(),
        pool = dbConfig.config("pool").let { poolConfig ->
            PoolConfig(
                maxSize = poolConfig.property("maxSize").getInt(),
                minIdle = poolConfig.property("minIdle").getInt(),
                maxLifetime = poolConfig.property("maxLifetime").getLong()
            )
        }
    )
}

Configuration Validation

Configuration Validation Utilities

// Validation extension functions
fun ApplicationConfigValue.requireInt(min: Int? = null, max: Int? = null): Int {
    val value = getInt()
    min?.let { require(value >= it) { "Value must be >= $it, got $value" } }
    max?.let { require(value <= it) { "Value must be <= $it, got $value" } }
    return value
}

fun ApplicationConfigValue.requireString(pattern: Regex? = null): String {
    val value = getString()
    require(value.isNotBlank()) { "Value cannot be blank" }
    pattern?.let { require(value.matches(it)) { "Value '$value' doesn't match pattern $it" } }
    return value
}

fun ApplicationConfig.requireProperty(path: String): ApplicationConfigValue {
    return propertyOrNull(path) ?: throw IllegalStateException("Required property '$path' not found")
}

// Validation example
fun Application.validateConfiguration() {
    val config = environment.config
    
    try {
        // Validate required properties
        val port = config.requireProperty("server.port").requireInt(min = 1, max = 65535)
        val host = config.requireProperty("server.host").requireString()
        val dbUrl = config.requireProperty("database.url")
            .requireString(Regex("^jdbc:[a-zA-Z0-9]+://.*"))
            
        log.info("Configuration validated successfully")
        log.info("Server will start on $host:$port")
        
    } catch (e: IllegalStateException) {
        log.error("Configuration validation failed: ${e.message}")
        throw e
    } catch (e: IllegalArgumentException) {
        log.error("Configuration validation failed: ${e.message}")  
        throw e
    }
}

Complete Configuration Example

Multi-Environment Configuration

// Environment-specific configuration loading
class ConfigurationManager {
    
    fun loadConfiguration(environment: String = "development"): ApplicationConfig {
        val configs = mutableListOf<ApplicationConfig>()
        
        // Base configuration
        configs.add(ConfigLoaders.classpath("application.conf"))
        
        // Environment-specific configuration
        val envConfigFile = "application-$environment.conf"
        try {
            configs.add(ConfigLoaders.classpath(envConfigFile))
        } catch (e: Exception) {
            println("Environment config $envConfigFile not found, using defaults")
        }
        
        // External configuration file (if exists)
        val externalConfig = File("config/application.conf")
        if (externalConfig.exists()) {
            configs.add(ConfigLoaders.file(externalConfig.path))
        }
        
        // Environment variables (highest priority)
        configs.add(ConfigLoaders.environment())
        
        // System properties (highest priority)
        configs.add(ConfigLoaders.systemProperties())
        
        return MergedApplicationConfig(*configs.toTypedArray())
    }
}

// Application configuration setup
fun main() {
    val environment = System.getenv("APP_ENV") ?: "development"
    val configManager = ConfigurationManager()
    val config = configManager.loadConfiguration(environment)
    
    val app = embeddedServer(Netty, environment = applicationEnvironment {
        this.config = config
        configure(config)
        developmentMode = config.developmentMode
    }) {
        configureApplication()
    }
    
    app.start(wait = true)
}

fun Application.configureApplication() {
    val config = environment.config
    
    // Validate configuration
    validateConfiguration()
    
    // Load type-safe configuration
    val serverConfig = config.getServerConfig()
    val dbConfig = config.getDatabaseConfig()
    
    // Configure application based on config
    configureDatabase(dbConfig)
    configureSecurity(config)
    configureRouting()
    
    log.info("Application configured successfully")
}

fun Application.configureDatabase(dbConfig: DatabaseConfig) {
    // Database configuration based on config
    log.info("Configuring database: ${dbConfig.url}")
}

fun Application.configureSecurity(config: ApplicationConfig) {
    val securityConfig = config.config("security")
    
    if (securityConfig.propertyOrNull("jwt.enabled")?.getBoolean() == true) {
        val jwtSecret = securityConfig.property("jwt.secret").getString()
        val jwtIssuer = securityConfig.property("jwt.issuer").getString()
        // Configure JWT
        log.info("JWT security configured")
    }
    
    if (securityConfig.propertyOrNull("cors.enabled")?.getBoolean() == true) {
        val allowedHosts = securityConfig.property("cors.allowedHosts").getList()
        // Configure CORS
        log.info("CORS configured for hosts: $allowedHosts")
    }
}

// Configuration files structure:
/*
resources/
├── application.conf                    # Base configuration
├── application-development.conf        # Development overrides  
├── application-staging.conf           # Staging overrides
├── application-production.conf        # Production overrides
└── logback.xml                        # Logging configuration

config/                                # External configuration
└── application.conf                   # Local overrides
*/

This comprehensive configuration documentation covers all aspects of Ktor's configuration system, from basic property access to advanced multi-environment configuration management with validation and type safety.

Install with Tessl CLI

npx tessl i tessl/maven-io-ktor--ktor-server-core

docs

application.md

configuration.md

engine.md

index.md

request-response.md

routing.md

tile.json