Skip to main content

Ktor

Ktor is a framework for building asynchronous servers and clients in connected systems using the powerful Kotlin programming language. We will use Ktor here, to build a simple web application.

Let's go 🚀

Get the code

Gradle Setup

First, add the Koin dependency like below:

dependencies {
// Koin for Kotlin apps
implementation "io.insert-koin:koin-ktor:$koin_version"
implementation "io.insert-koin:koin-logger-slf4j:$koin_version"
}

Application Overview

The idea of the application is to manage a list of users, and display it in our UserApplication class:

Users -> UserRepository -> UserService -> UserApplication

The "User" Data

We will manage a collection of Users. Here is the data class:

data class User(val name : String)

We create a "Repository" component to manage the list of users (add users or find one by name). Here below, the UserRepository interface and its implementation:

interface UserRepository {
fun findUser(name : String): User?
fun addUsers(users : List<User>)
}

class UserRepositoryImpl : UserRepository {

private val _users = arrayListOf<User>()

override fun findUser(name: String): User? {
return _users.firstOrNull { it.name == name }
}

override fun addUsers(users : List<User>) {
_users.addAll(users)
}
}

The Koin module

Use the module function to declare a Koin module. A Koin module is the place where we define all our components to be injected.

val appModule = module {

}

Let's declare our first component. We want a singleton of UserRepository, by creating an instance of UserRepositoryImpl

val appModule = module {
single<UserRepository> { UserRepositoryImpl() }
}

The UserService Component

Let's write the UserService component to request the default user:

class UserService(private val userRepository: UserRepository) {

fun getDefaultUser() : User = userRepository.findUser(DefaultData.DEFAULT_USER.name) ?: error("Can't find default user")
}

UserRepository is referenced in UserPresenter`s constructor

We declare UserService in our Koin module. We declare it as a single definition:

val appModule = module {
single<UserRepository> { UserRepositoryImpl() }
single { UserService(get()) }
}

HTTP Controller

Finally, we need an HTTP Controller to create the HTTP Route. In Ktor is will be expressed through an Ktor extension function:

fun Application.main() {

// Lazy inject HelloService
val service by inject<UserService>()

// Routing section
routing {
get("/hello") {
call.respondText(service.sayHello())
}
}
}

Check that your application.conf is configured like below, to help start the Application.main function:

ktor {
deployment {
port = 8080

// For dev purpose
//autoreload = true
//watch = [org.koin.sample]
}

application {
modules = [ org.koin.sample.UserApplicationKt.main ]
}
}

Declare your dependencies

Let's assemble our components with a Koin module:

val appModule = module {
singleOf(::UserRepositoryImpl) { bind<UserRepository>() }
singleOf(::UserService)
}

Start and Inject

Finally, let's start Koin from Ktor:

fun Application.main() {
install(Koin) {
slf4jLogger()
modules(appModule)
}

// Lazy inject HelloService
val service by inject<UserService>()
service.saveDefaultUsers()

// Routing section
routing {
get("/hello") {
call.respondText(service.sayHello())
}
}
}

Let's start Ktor:

fun main(args: Array<String>) {
// Start Ktor
embeddedServer(Netty, commandLineEnvironment(args)).start(wait = true)
}

That's it! You're ready to go. Check the http://localhost:8080/hello url!