Ktor server patterns: routing DSL, plugins, authentication, Koin DI, kotlinx.serialization, WebSockets, and testApplication testing. USE WHEN building Ktor HTTP servers or REST APIs, wiring Koin DI, configuring Auth/CORS/ContentNegotiation plugins, or writing Ktor integration tests.
Ktor server patterns: routing DSL, plugins, authentication, Koin DI, kotlinx.serialization, WebSockets, and testApplication testing. USE WHEN building Ktor HTTP servers or REST APIs, wiring Koin DI, configuring Auth/CORS/ContentNegotiation plugins, or writing Ktor integration tests.
cluster
jvm
version
1.0.0
Ktor Server Patterns
Comprehensive Ktor patterns for building robust, maintainable HTTP servers with Kotlin coroutines.
fun Route.userRoutes() {
val userService by inject<UserService>()
route("/users") {
get {
val users = userService.getAll()
call.respond(ApiResponse.ok(users))
}
}
}
// Validate request data in routesfun Route.userRoutes() {
val userService by inject<UserService>()
post("/users") {
val request = call.receive<CreateUserRequest>()
// Validate
require(request.name.isNotBlank()) { "Name is required" }
require(request.name.length <= 100) { "Name must be 100 characters or less" }
require(request.email.matches(Regex(".+@.+\\..+"))) { "Invalid email format" }
val user = userService.create(request)
call.respond(HttpStatusCode.Created, ApiResponse.ok(user))
}
}
// Or use a validation extensionfun CreateUserRequest.validate() {
require(name.isNotBlank()) { "Name is required" }
require(name.length <= 100) { "Name must be 100 characters or less" }
require(email.matches(Regex(".+@.+\\..+"))) { "Invalid email format" }
}
WebSockets
fun Application.configureWebSockets() {
install(WebSockets) {
pingPeriod = 15.seconds
timeout = 15.seconds
maxFrameSize = 64 * 1024// 64 KiB — increase only if your protocol requires larger frames
masking = false// Server-to-client frames are unmasked per RFC 6455; client-to-server are always masked by Ktor
}
}
fun Route.chatRoutes() {
val connections = Collections.synchronizedSet<Connection>(LinkedHashSet())
webSocket("/chat") {
val thisConnection = Connection(this)
connections += thisConnection
try {
send("Connected! Users online: ${connections.size}")
for (frame in incoming) {
frame as? Frame.Text ?: continueval text = frame.readText()
val message = ChatMessage(thisConnection.name, text)
// Snapshot under lock to avoid ConcurrentModificationExceptionval snapshot = synchronized(connections) { connections.toList() }
snapshot.forEach { conn ->
conn.session.send(Json.encodeToString(message))
}
}
} catch (e: Exception) {
logger.error("WebSocket error", e)
} finally {
connections -= thisConnection
}
}
}
dataclassConnection(val session: DefaultWebSocketSession) {
val name: String = "User-${counter.getAndIncrement()}"companionobject {
privateval counter = AtomicInteger(0)
}
}
fun Application.configureDI() {
val dbUrl = environment.config.property("database.url").getString()
val dbDriver = environment.config.property("database.driver").getString()
val maxPoolSize = environment.config.property("database.maxPoolSize").getString().toInt()
install(Koin) {
modules(module {
single { DatabaseConfig(dbUrl, dbDriver, maxPoolSize) }
single { DatabaseFactory.create(get()) }
})
}
}
Quick Reference: Ktor Patterns
Pattern
Description
route("/path") { get { } }
Route grouping with DSL
call.receive<T>()
Deserialize request body
call.respond(status, body)
Send response with status
call.parameters["id"]
Read path parameters
call.request.queryParameters["q"]
Read query parameters
install(Plugin) { }
Install and configure plugin
authenticate("name") { }
Protect routes with auth
by inject<T>()
Koin dependency injection
testApplication { }
Integration testing
Remember: Ktor is designed around Kotlin coroutines and DSLs. Keep routes thin, push logic to services, and use Koin for dependency injection. Test with testApplication for full integration coverage.