0
# Configuration Management
1
2
Hierarchical configuration system with type conversion, environment-specific settings, and support for multiple configuration sources including files, environment variables, and programmatic configuration.
3
4
## Capabilities
5
6
### Application Configuration
7
8
Core interface for accessing application configuration with hierarchical property access and type conversion.
9
10
```kotlin { .api }
11
/**
12
* Interface for application configuration access
13
*/
14
interface ApplicationConfig {
15
/** Get configuration property by path */
16
fun property(path: String): ApplicationConfigValue
17
/** Get optional configuration property */
18
fun propertyOrNull(path: String): ApplicationConfigValue?
19
/** Get nested configuration section */
20
fun config(path: String): ApplicationConfig
21
/** Get list of configuration sections */
22
fun configList(path: String): List<ApplicationConfig>
23
/** Get all configuration keys at current level */
24
fun keys(): Set<String>
25
/** Convert configuration to map */
26
fun toMap(): Map<String, Any?>
27
}
28
29
/**
30
* Configuration value with type conversion
31
*/
32
interface ApplicationConfigValue {
33
/** Get value as string */
34
fun getString(): String
35
/** Get value as list of strings */
36
fun getList(): List<String>
37
}
38
39
/** Access configuration from application call */
40
val ApplicationCall.config: ApplicationConfig get() = application.environment.config
41
```
42
43
### Configuration Value Extensions
44
45
Extension functions for type-safe configuration value access with default values and validation.
46
47
```kotlin { .api }
48
/**
49
* Get configuration value as Int
50
* @param default - Default value if property not found or conversion fails
51
* @return Integer value or default
52
*/
53
fun ApplicationConfigValue.getInt(default: Int = 0): Int = try {
54
getString().toInt()
55
} catch (e: Exception) {
56
default
57
}
58
59
/**
60
* Get configuration value as Long
61
*/
62
fun ApplicationConfigValue.getLong(default: Long = 0L): Long = try {
63
getString().toLong()
64
} catch (e: Exception) {
65
default
66
}
67
68
/**
69
* Get configuration value as Boolean
70
*/
71
fun ApplicationConfigValue.getBoolean(default: Boolean = false): Boolean = try {
72
getString().toBoolean()
73
} catch (e: Exception) {
74
default
75
}
76
77
/**
78
* Get configuration value as Double
79
*/
80
fun ApplicationConfigValue.getDouble(default: Double = 0.0): Double = try {
81
getString().toDouble()
82
} catch (e: Exception) {
83
default
84
}
85
86
/**
87
* Get required configuration value as Int
88
* @throws ConfigurationException if property not found or conversion fails
89
*/
90
fun ApplicationConfigValue.getIntOrFail(): Int = try {
91
getString().toInt()
92
} catch (e: Exception) {
93
throw ConfigurationException("Cannot convert configuration value to Int", e)
94
}
95
96
/**
97
* Get required configuration value as Long
98
*/
99
fun ApplicationConfigValue.getLongOrFail(): Long = try {
100
getString().toLong()
101
} catch (e: Exception) {
102
throw ConfigurationException("Cannot convert configuration value to Long", e)
103
}
104
105
/**
106
* Get required configuration value as Boolean
107
*/
108
fun ApplicationConfigValue.getBooleanOrFail(): Boolean = try {
109
getString().toBoolean()
110
} catch (e: Exception) {
111
throw ConfigurationException("Cannot convert configuration value to Boolean", e)
112
}
113
```
114
115
### Configuration Implementations
116
117
Concrete implementations of ApplicationConfig for different configuration sources and patterns.
118
119
```kotlin { .api }
120
/**
121
* Map-based implementation of ApplicationConfig
122
*/
123
class MapApplicationConfig : ApplicationConfig {
124
/** Create config from map */
125
constructor(map: Map<String, Any>)
126
/** Create config from vararg pairs */
127
constructor(vararg values: Pair<String, Any>)
128
129
/** Put configuration value */
130
fun put(path: String, value: String)
131
/** Put all values from map */
132
fun putAll(values: Map<String, Any>)
133
/** Remove configuration value */
134
fun remove(path: String)
135
/** Clear all configuration */
136
fun clear()
137
}
138
139
/**
140
* Merges multiple configuration sources with precedence
141
*/
142
class MergedApplicationConfig(vararg sources: ApplicationConfig) : ApplicationConfig {
143
/** Add configuration source with higher precedence */
144
fun withFallback(config: ApplicationConfig): MergedApplicationConfig
145
}
146
147
/**
148
* Empty configuration implementation
149
*/
150
object EmptyApplicationConfig : ApplicationConfig
151
```
152
153
### Configuration Loading
154
155
Utilities for loading configuration from various sources including files, environment variables, and system properties.
156
157
```kotlin { .api }
158
/**
159
* Load configuration from HOCON file
160
* @param file - Configuration file to load
161
* @return ApplicationConfig instance
162
*/
163
fun loadApplicationConfig(file: File): ApplicationConfig
164
165
/**
166
* Load configuration from resource
167
* @param resource - Resource path to load
168
* @return ApplicationConfig instance
169
*/
170
fun loadApplicationConfig(resource: String): ApplicationConfig
171
172
/**
173
* Load configuration from multiple sources
174
* @param sources - Configuration sources in priority order
175
* @return Merged configuration
176
*/
177
fun loadApplicationConfig(vararg sources: ApplicationConfig): ApplicationConfig
178
179
/**
180
* Create configuration from environment variables
181
* @param prefix - Environment variable prefix to filter
182
* @return Configuration from environment
183
*/
184
fun environmentConfig(prefix: String = ""): ApplicationConfig
185
186
/**
187
* Create configuration from system properties
188
* @param prefix - System property prefix to filter
189
* @return Configuration from system properties
190
*/
191
fun systemPropertiesConfig(prefix: String = ""): ApplicationConfig
192
```
193
194
### Configuration Validation
195
196
Utilities for validating configuration values and providing meaningful error messages.
197
198
```kotlin { .api }
199
/**
200
* Configuration validation exception
201
*/
202
class ConfigurationException(message: String, cause: Throwable? = null) : Exception(message, cause)
203
204
/**
205
* Validate configuration property exists
206
* @param path - Configuration path to check
207
* @throws ConfigurationException if property not found
208
*/
209
fun ApplicationConfig.requireProperty(path: String): ApplicationConfigValue {
210
return propertyOrNull(path) ?: throw ConfigurationException("Required configuration property '$path' not found")
211
}
212
213
/**
214
* Validate configuration section exists
215
* @param path - Configuration section path to check
216
* @throws ConfigurationException if section not found
217
*/
218
fun ApplicationConfig.requireConfig(path: String): ApplicationConfig {
219
return try {
220
config(path)
221
} catch (e: Exception) {
222
throw ConfigurationException("Required configuration section '$path' not found", e)
223
}
224
}
225
226
/**
227
* Validate configuration value is in allowed set
228
* @param allowedValues - Set of allowed values
229
* @throws ConfigurationException if value not in set
230
*/
231
fun ApplicationConfigValue.requireOneOf(vararg allowedValues: String): String {
232
val value = getString()
233
if (value !in allowedValues) {
234
throw ConfigurationException("Configuration value '$value' must be one of: ${allowedValues.joinToString()}")
235
}
236
return value
237
}
238
```
239
240
### Configuration Scopes
241
242
Scoped configuration access for different parts of the application with inheritance and overrides.
243
244
```kotlin { .api }
245
/**
246
* Scoped configuration access
247
*/
248
class ConfigurationScope(
249
private val config: ApplicationConfig,
250
private val path: String = ""
251
) {
252
/** Get property in current scope */
253
fun property(name: String): ApplicationConfigValue = config.property(scopedPath(name))
254
/** Get optional property in current scope */
255
fun propertyOrNull(name: String): ApplicationConfigValue? = config.propertyOrNull(scopedPath(name))
256
/** Create nested scope */
257
fun scope(name: String): ConfigurationScope = ConfigurationScope(config, scopedPath(name))
258
259
private fun scopedPath(name: String) = if (path.isEmpty()) name else "$path.$name"
260
}
261
262
/**
263
* Create configuration scope for specific section
264
* @param path - Section path for scope
265
* @return Scoped configuration access
266
*/
267
fun ApplicationConfig.scope(path: String): ConfigurationScope = ConfigurationScope(this, path)
268
```
269
270
**Usage Examples:**
271
272
```kotlin
273
import io.ktor.server.application.*
274
import io.ktor.server.config.*
275
import io.ktor.server.response.*
276
import io.ktor.server.routing.*
277
278
// Basic configuration access
279
fun Application.basicConfigExample() {
280
// Access configuration values
281
val appName = environment.config.property("app.name").getString()
282
val port = environment.config.property("server.port").getInt(8080)
283
val debugMode = environment.config.propertyOrNull("app.debug")?.getBoolean() ?: false
284
285
log.info("Starting $appName on port $port (debug: $debugMode)")
286
287
routing {
288
get("/config") {
289
call.respondText("App: $appName, Port: $port, Debug: $debugMode")
290
}
291
}
292
}
293
294
// Configuration with validation
295
fun Application.validatedConfigExample() {
296
try {
297
// Required configuration
298
val dbUrl = environment.config.requireProperty("database.url").getString()
299
val dbDriver = environment.config.requireProperty("database.driver").getString()
300
301
// Optional with defaults
302
val connectionTimeout = environment.config.propertyOrNull("database.timeout")?.getInt() ?: 30
303
val maxConnections = environment.config.propertyOrNull("database.maxConnections")?.getInt() ?: 10
304
305
// Validated enums
306
val logLevel = environment.config.requireProperty("logging.level")
307
.requireOneOf("DEBUG", "INFO", "WARN", "ERROR")
308
309
log.info("Database: $dbUrl (timeout: ${connectionTimeout}s, max connections: $maxConnections)")
310
log.info("Log level: $logLevel")
311
312
} catch (e: ConfigurationException) {
313
log.error("Configuration error: ${e.message}")
314
throw e
315
}
316
}
317
318
// Nested configuration sections
319
fun Application.nestedConfigExample() {
320
// Database configuration section
321
val dbConfig = environment.config.config("database")
322
val dbUrl = dbConfig.property("url").getString()
323
val dbUser = dbConfig.property("user").getString()
324
val dbPassword = dbConfig.property("password").getString()
325
326
// Server configuration list
327
val serverConfigs = environment.config.configList("servers")
328
for ((index, serverConfig) in serverConfigs.withIndex()) {
329
val host = serverConfig.property("host").getString()
330
val port = serverConfig.property("port").getInt()
331
log.info("Server $index: $host:$port")
332
}
333
334
routing {
335
get("/servers") {
336
val servers = serverConfigs.map { server ->
337
mapOf(
338
"host" to server.property("host").getString(),
339
"port" to server.property("port").getInt()
340
)
341
}
342
call.respond(servers)
343
}
344
}
345
}
346
347
// Programmatic configuration
348
fun Application.programmaticConfigExample() {
349
// Create configuration from map
350
val appConfig = MapApplicationConfig(
351
"app.name" to "My Ktor App",
352
"app.version" to "1.0.0",
353
"server.host" to "localhost",
354
"server.port" to "8080"
355
)
356
357
// Merge with environment variables
358
val envConfig = environmentConfig("KTOR_")
359
val mergedConfig = MergedApplicationConfig(envConfig, appConfig)
360
361
// Access merged configuration
362
val appName = mergedConfig.property("app.name").getString()
363
val host = mergedConfig.property("server.host").getString()
364
val port = mergedConfig.property("server.port").getInt()
365
366
log.info("App: $appName running on $host:$port")
367
}
368
369
// Configuration scopes
370
fun Application.scopedConfigExample() {
371
// Create database scope
372
val dbScope = environment.config.scope("database")
373
val url = dbScope.property("url").getString()
374
val user = dbScope.property("user").getString()
375
376
// Create cache scope
377
val cacheScope = environment.config.scope("cache")
378
val cacheSize = cacheScope.property("size").getInt(1000)
379
val cacheTtl = cacheScope.property("ttl").getLong(3600)
380
381
// Nested scopes
382
val redisScope = cacheScope.scope("redis")
383
val redisHost = redisScope.property("host").getString()
384
val redisPort = redisScope.property("port").getInt(6379)
385
386
log.info("Database: $url (user: $user)")
387
log.info("Cache: size=$cacheSize, ttl=${cacheTtl}s")
388
log.info("Redis: $redisHost:$redisPort")
389
}
390
391
// Route-specific configuration access
392
fun Application.routeConfigExample() {
393
routing {
394
get("/admin") {
395
// Access configuration within route
396
val adminEnabled = call.config.propertyOrNull("admin.enabled")?.getBoolean() ?: false
397
if (!adminEnabled) {
398
call.respond(HttpStatusCode.NotFound)
399
return@get
400
}
401
402
val adminTitle = call.config.property("admin.title").getString()
403
call.respondText("Welcome to $adminTitle")
404
}
405
406
get("/api/limits") {
407
val rateLimit = call.config.property("api.rateLimit").getInt(100)
408
val maxPayload = call.config.property("api.maxPayloadSize").getLong(1024 * 1024)
409
410
call.respond(mapOf(
411
"rateLimit" to rateLimit,
412
"maxPayloadSize" to maxPayload
413
))
414
}
415
}
416
}
417
```
418
419
**Example Configuration File (application.conf):**
420
421
```hocon
422
app {
423
name = "My Ktor Application"
424
version = "1.0.0"
425
debug = false
426
}
427
428
server {
429
host = "0.0.0.0"
430
port = 8080
431
}
432
433
database {
434
url = "jdbc:postgresql://localhost:5432/myapp"
435
driver = "org.postgresql.Driver"
436
user = "appuser"
437
password = "secret"
438
timeout = 30
439
maxConnections = 20
440
}
441
442
cache {
443
size = 10000
444
ttl = 3600
445
redis {
446
host = "localhost"
447
port = 6379
448
}
449
}
450
451
logging {
452
level = "INFO"
453
}
454
455
servers = [
456
{
457
host = "server1.example.com"
458
port = 8080
459
},
460
{
461
host = "server2.example.com"
462
port = 8080
463
}
464
]
465
466
admin {
467
enabled = true
468
title = "Admin Panel"
469
}
470
471
api {
472
rateLimit = 1000
473
maxPayloadSize = 5242880 # 5MB
474
}
475
```