Building a Kotlin Back-End in 15 minutes

Donal O'Callaghan
4 min readSep 17, 2018
Photo by Johannes Plenio on Unsplash

The complete source code for this project is available on GitHub.

Introduction

You may already be enjoying the power and simplicity of Kotlin for writing Android applications but it turns out it’s also a very capable back-end language.

To demonstrate how straightforward it can be, we will take 15 minutes and build a simple API, a coin flipper.

When /flip is called it will simply return the result of a randomised coin flip:

curl localhost:8080/flip{
"face": "TAILS"
}

We will cache the previous results in an in-memory database, the contents of which can be retrieved by calling /outcomes:

curl localhost:8080/outcomes[
{
"face": "HEADS"
},
{
"face": "TAILS"
},
{
"face": "HEADS"
}
]
Photo by Ryan Thomas Ang on Unsplash

Components

For the server we will use Ktor, a framework from JetBrains. It makes setting up a server and adding functionality extremely straightforward.

For the database access we will use Exposed, another JetBrains creation which wraps a JDBC driver to expose SQL functionality in Kotlin.

For the in-memory database we will use H2, a lightweight java implementation.

Dependencies

Starting with an empty gradle project, in the build.gradle we add the dependencies for:

  • Ktor. We use Netty for the server but support for others is available. We also need JSON support, I prefer Gson for this:
compile "io.ktor:ktor-server-netty:$ktor_version"
compile "io.ktor:ktor-gson:$ktor_version"
  • Exposed:
compile "org.jetbrains.exposed:exposed:$exposed_version"
  • H2 Database:
compile "com.h2database:h2:$h2_version"
  • Ktor uses coroutines so these must be enabled in the build file:
kotlin { experimental { coroutines "enable" } }

Server Definition Code

For the server code we start by creating a new Kotlin file, let’s call it Main.kt, and add a main function.

Within this main function we can start the server, running on port 8080.

We also add a functional source of random booleans to use for flipping the imaginary coin:

fun main(args: Array<String>) {
val secureRandom = SecureRandom()
val booleanRandomiser = { secureRandom.nextBoolean() }

val server = embeddedServer(Netty, port = 8080) {
}
}

Adding support for Gson is as simple as adding this initialiser to the embeddedServer block:

.
.
install(ContentNegotiation) {
gson {
setPrettyPrinting() // Outputs nicely formatted JSON
}
}
.
.

Listening for Requests

Ktor defines a helpful DSL for handling requests and responses in the form of routing, this makes is easy to add listeners for GET, POST, PUT and DELETE requests etc.

Let’s add our GET endpoints for /flip and /outcomes to the embeddedServer block:

.
.
routing {
get("/flip") {
handleRootRequest(booleanRandomiser)
}
get("/outcomes") {
handleGetOutcomesRequest()
}
}
.
.

It’s that simple!

Database Creation

In the top-level Main.kt file we add the database creation logic using the exposed framework.

We’ll define a simple table (RESULTS) with a single column to hold the outcomes of flips (“heads” or “tails”).

We also add definitions of Coin object and Face enum:

object RESULTS : org.jetbrains.exposed.sql.Table() {
val face = varchar("face", 5) // "face" is the column name, max 5 chars
}
data class Coin(var face: Face)enum class Face { HEADS, TAILS }

Back in the main function we add the code to create the in-memory database and start the server.

Database
.connect("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1","org.h2.Driver")
// Database accesses are wrapped in a transaction block
transaction { create(RESULTS) }
server.start(wait = true)

Sending the responses

Flip request

Let’s implement the handleFlipRequest method we referenced above. There are three parts to this:

  • Create a random HEADS or TAILS Face
  • Insert the outcome into the database
  • Create a Coin object and return the result as a JSON string
private suspend fun PipelineContext<Unit, ApplicationCall>
.handleFlipRequest(booleanRandomiser: () -> Boolean) {

// Create a random Face
val result = booleanRandomiser.invoke()
val faceValue: Face = if (result) Face.HEADS else Face.TAILS

runBlocking {
// Insert the result in the database
transaction {
RESULTS.insert { it[face] = faceValue.name }
}
}.apply {
// Pass the response back to the client
call.respond(Coin(faceValue))
}

Now let’s implement the handleGetOutcomesRequest we referenced above.

This has two parts

  • Get all the outcomes from the database
  • Return the data to the client
private suspend fun 
PipelineContext<Unit, ApplicationCall>.handleGetOutcomesRequest() {
var allOutcomes: List<Coin> = ArrayList()

// Select all the entries in the DB, mapping the "face" value in each row to a coin
transaction {
allOutcomes = RESULTS.selectAll().map {
resultRow -> Coin(Face.valueOf(resultRow[RESULTS.face].toString()))
}
}

// This is a coroutine so we can call .apply to handle what happens next, in this case replying to the client
.apply {
call.respond(allOutcomes) // Gson turns the list into a nice JSON array
}
}

To run the service you can either use the IDE or simply use the gradle wrapper:

./gradlew run

That’s it! Have fun with Kotlin for the back-end!

--

--