Part 6 · 1 chapters · ~8 min

Ktor End to End

A Ktor service from scratch: engines, plugins (content negotiation with kotlinx.serialization, authentication, status pages, call logging), the routing DSL, request validation, dependency injection with Koin or by hand, database access with Exposed and HikariCP, idempotency keys, OpenAPI, and packaging.

7

A transfers endpoint in Ktor

code
fun Application.module() {
    install(ContentNegotiation) { json() }
    install(StatusPages) { exception<IllegalArgumentException> { call, e -> call.respond(HttpStatusCode.UnprocessableEntity, mapOf("error" to e.message)) } }
    install(Authentication) { bearer("api") { authenticate { creds -> apiKeys.principal(creds.token) } } }
    routing {
        authenticate("api") {
            post("/v1/transfers") {
                val key = call.request.header("Idempotency-Key") ?: return@post call.respond(HttpStatusCode.BadRequest)
                val body = call.receive<CreateTransferRequest>()
                when (val r = transfers.create(body, key)) {
                    is TransferResult.Ok -> call.respond(HttpStatusCode.Created, r.transfer.toDto())
                    is TransferResult.InsufficientFunds -> call.respond(HttpStatusCode.UnprocessableEntity, mapOf("error" to "insufficient_funds"))
                    TransferResult.LimitExceeded -> call.respond(HttpStatusCode.UnprocessableEntity, mapOf("error" to "limit_exceeded"))
                }
            }
        }
    }
}
fun main() = embeddedServer(Netty, port = 8080, module = Application::module).start(wait = true)
KTOR END TO END
a coroutine-native web framework from JetBrains
engineNetty or CIOpluginsContentNegotiation, Auth, StatusPagesroutingpost("/v1/transfers")servicesuspend functionsExposed / jOOQ / JDBCon Dispatchers.IO
swipe the figure sideways, or tap expand for full screen
1/4
engine and plugins
Ktor runs on an engine (Netty, CIO) and is assembled from plugins: you install only what you use, keeping services small.
pick your pluginssmall by default