Ktor — Deep Dive

Akshay Shah
4 min readJan 23, 2021

--

In my previous article Ktor — Backend made simple we covered how simple and easy it was to set up a Ktor server and get your APIs running in less than 20 minutes but what we forgot to explain was, how Ktor really works? In this article, we will primarily focus on the basics of how Ktor works? What is going on, under the hood?

Ktor Application

A Ktor application can be used to make a variety of Server and Client applications including but not limited to static web pages, dynamic web pages, HTTP endpoint, a RESTful system or even writing a microservice.

Request and Response pipeline

When a request comes in it is captured by the routing mechanism which redirects it to the route handler. Route handler consists of our application logic which decides what is to be done with the request whether to respond with a simple text or to something complex like a JSON response or to something like storing in a database. Finally, handler should respond to the request.

Entry points

You can run a Ktor application in several ways:

  • With a plain main by calling embeddedServer something like this
fun main(args: Array<String>) {
embeddedServer(Netty, port = 8080) {
routing {
get("/") {
call.respondText("Hello, Ktor!")
}
}
}.start(true)
}
fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)fun Application.module() {
routing {
get
("/") {
call.respondText("Hello, Ktor!")
}
}

}

Application Engine

The application engine can be considered to be the heart of the Ktor server, it is in charge of running the application. It uses the configuration provided to listen, to the right ports and hosts, by using SSL, certificates, and so on, with the specified workers. There are multiple ApplicationEngine for supported servers like Netty, Jetty, CIO, Tomcat, etc.

Application Pipeline

It is created by the ApplicationEngineEnvironment and it is initially empty. It is a pipeline without a subject that has ApplicationCall as the context. Each specified module will be called to configure this application when the environment is created.

Pipelines

Ktor defines pipelines for asynchronous computations. We will find pipelines all over Ktor. Pipelines have an associated subject type, context type, and a list of phases with interceptors associated with them.

Features

A feature is something that you can install to a specific pipeline.

As the request comes in it is redirected to the correct handler via routing mechanism but before it is redirected to the handler it goes through a number of features sitting between the request and the handler. After which the handler works on the request and again it is going through multiple features before finally the response is sent to the client. For example the diagram below

Here, even routing can be considered to be a feature along with encoding. Features are designed to offer maximum flexibility. They are easy to install and the code looks something like this

install(Routing)
install(Encoding)

Modules

A Ktor module is just a user-defined function receiving the Applicationclass that is in charge of configuring the server pipeline, installing features, registering routes, handling requests, etc. You have to define the modules to be loaded in application.conf when the server starts up which should look something like this

ktor {
deployment {
port = 8080
port = ${?PORT}
}
application {
modules = [ com.akshayshah.ktorexample.ApplicationKt.module ]
}
}
//application.conf

and your Application.ktfile should look something like this

fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)

fun Application.module(testing: Boolean = false) {
routing {
get
("/") {
call.respondText("Hello, Ktor!")
}
}

}

Routing

Routing is a critical part of a Ktor application. It defines the mechanism to handle the incoming request. When a request like /users is made it defines how we want his request to be served. A simple routing could be like this

routing {
route("/users", HttpMethod.Get) {
handle {
call.respondText("Akshay Shah")
}
}
}

routing takes in 3 parameters

  • url pattern
  • type of request GET, POST, PUT, DELETE, HEAD, OPTION, or PATCH
  • handler, to handle the incoming request

All said and done I think I can conclude this article and would request you to show your love by clapping and sharing this article in large numbers to all the Kotlin enthusiasts. Follow me here or on LinkedIn, I will be coming up with more such articles primarily focusing on Ktor, KMM, and Kotlin/JS. Full code can be found on my Github repo.

--

--

Akshay Shah

Lead Software Engineer @ EPAM Systems |Android | Kotlin | KMM