What You Will Learn

  • Setting up Kotlin with Maven
  • Creating a Javalin/Kotlin CRUD REST API (no database)
  • Some neat Kotlin features (from a Java developer’s perspective)

The instructions for this tutorial will focus on IntelliJ IDEA, as it’s made by JetBrains, the same people who make Kotlin. We recommend downloading the free community edition of IDEA while following this tutorial, but there is also Kotlin support in Eclipse.

Setting up Kotlin with Maven (in IntelliJ IDEA)

The good people over at JetBrains have an up-to-date archetype for Kotlin. To use it, do as follows:

  • File -> New -> Project
  • Maven -> Create from archetype -> org.jetbrains.kotlin:kotlin-archetype-jvm -> Next
  • Follow the instructions and pick a project name
  • Create src/main/kotlin/app/Main.kt

There is no public static void main(String[] args) in Kotlin, instead you have a fun main(args: Array<String>).

fun main() { // can omit args
    println("Hello, world!")
}
You'll have to point to the file (not class) containing this main function (not method) from your pom.xml if you want to build a jar. Doing this is not necessary for this tutorial, but the code on GitHub demonstrates how to do it for those interested.

Using Javalin with Kotlin

Add the dependency:

  • Maven
  • Gradle
  • SBT
  • Grape
  • Leiningen
  • Buildr
  • Ivy
<dependency>
    <groupId>io.javalin</groupId>
    <artifactId>javalin</artifactId>
    <version>6.1.3</version>
</dependency>

Not familiar with Maven? Read our Maven tutorial.

implementation("io.javalin:javalin:6.1.3")

Not familiar with Gradle? Read our Gradle tutorial.

libraryDependencies += "io.javalin" % "javalin" % "6.1.3"
@Grab(group='io.javalin', module='javalin', version='6.1.3')
[io.javalin/javalin "6.1.3"]
'io.javalin:javalin:jar:6.1.3'
<dependency org="io.javalin" name="javalin" rev="6.1.3" />

If you want Javalin with testing tools, Jackson and Logback, you can use the artifact id javalin-bundle instead of javalin.

And paste the “Hello world” example:

import io.javalin.Javalin

fun main() {
    val app = Javalin.create().start(7070)
    app.get("/") { ctx -> ctx.result("Hello World") }
}

It looks pretty similar to Java8:
Java8: get("/path", ctx -> { ... });
Kotlin: get("/path") { ctx -> ...}.

The syntax (){} might look a little strange to Java programmers. Kotlin supports trailing closures and provides semicolon inference. Simplified, this means you don’t have to wrap closures in parentheses and end statements with semicolons.

Creating a Javalin/Kotlin CRUD microservice

Kotlin data-classes

Kotlin has a really neat feature called Data classes. To create a data class you just have to write:

data class User(val name: String, val email: String, val id: Int)

… and you’re done! If you declare all parameters as val you get an immutable class similar to the Lombok @Value annotation, only better. Regardless of if you use var or val (or a mix) for your data class, you get toString, hashCode/equals, copying and destructuring included:

val alice = User(name = "Alice", email = "[email protected]", id = 0)
val aliceNewEmail = alice.copy(email = "[email protected]") // new object with only email changed

val (name, email) = aliceNewEmail // choose the fields you want
println("$name's new email is $email") // prints "Alice's new email is [email protected]"

Initializing some data

Let’s initialize our fake user-database with four users:

val users = mapOf(
    0 to User(name = "Alice", email = "[email protected]", id = 0),
    1 to User(name = "Bob", email = "[email protected]", id = 1),
    2 to User(name = "Carol", email = "[email protected]", id = 2),
    3 to User(name = "Dave", email = "[email protected]", id = 3)
)

Kotlin has type inference and named parameters (we could have written our arguments in any order). It also has a nice standard library providing map-literal-like functions.

Creating a data access object

We need to be able to read out data somehow, so let’s set up some basic CRUD functionality, with one added function for finding user by email:

import java.util.concurrent.atomic.AtomicInteger

class UserDao {

    // "Initialize" with a few users
    // This demonstrates type inference, map-literals and named parameters
    val users = hashMapOf(
            0 to User(name = "Alice", email = "[email protected]", id = 0),
            1 to User(name = "Bob", email = "[email protected]", id = 1),
            2 to User(name = "Carol", email = "[email protected]", id = 2),
            3 to User(name = "Dave", email = "[email protected]", id = 3)
    )

    var lastId: AtomicInteger = AtomicInteger(users.size - 1)

    fun save(name: String, email: String) {
        val id = lastId.incrementAndGet()
        users[id] = User(name = name, email = email, id = id)
    }

    fun findById(id: Int): User? {
        return users[id]
    }

    fun findByEmail(email: String): User? {
        return users.values.find { it.email == email } // == is equivalent to java .equals() (referential equality is checked by ===)
    }

    fun update(id: Int, user: User) {
        users[id] = User(name = user.name, email = user.email, id = id)
    }

    fun delete(id: Int) {
        users.remove(id)
    }

}

The findByEmail function shows of some neat features. In addition to the trailing closures that we saw earlier, Kotlin also has a very practical find function and a special it keyword, which replaces user -> user style declarations with just it (docs). The function also demonstrates that == is the structural equality operator for Strings in Kotlin (equivalent to .equals() in Java). If you want to check for referential equality in Kotlin you can use ===. Another thing worth noticing is that the find-functions return User?, which means the function will return either a User or null. In Kotlin you have to specify the possibility of a null-return.

findByEmail(), Kotlin vs Java:

// Kotlin
fun findByEmail(email: String): User? {
    return users.values.find { it.email == email }
}

// Java
public User findByEmail(String email) {
    return users.values().stream()
            .filter(user -> user.getEmail().equals(email))
            .findFirst()
            .orElse(null);
}

Creating the REST API

Kotlin and Javalin play very well together (in fact, Kotlin seems to play well with all Java dependencies).

import app.user.User
import app.user.UserDao
import io.javalin.apibuilder.ApiBuilder.*
import io.javalin.Javalin
import io.javalin.http.HttpStatus
import io.javalin.http.NotFoundResponse
import io.javalin.http.bodyAsClass
import io.javalin.http.pathParamAsClass

fun main() {

    val userDao = UserDao()

    val app = Javalin.create {
        it.router.apiBuilder {

            get("/") { it.redirect("/users") } // redirect root to /users

            get("/users") { ctx ->
                ctx.json(userDao.users)
            }

            get("/users/{user-id}") { ctx ->
                val userId = ctx.pathParamAsClass<Int>("user-id").get()
                val user = userDao.findById(userId) ?: throw NotFoundResponse()
                ctx.json(user)
            }

            get("/users/email/{email}") { ctx ->
                val email = ctx.pathParam("email")
                val user = userDao.findByEmail(email) ?: throw NotFoundResponse()
                ctx.json(user)
            }

            post("/users") { ctx ->
                val user = ctx.bodyAsClass<User>()
                userDao.save(name = user.name, email = user.email)
                ctx.status(201)
            }

            patch("/users/{user-id}") { ctx ->
                val userId = ctx.pathParamAsClass<Int>("user-id").get()
                val user = ctx.bodyAsClass<User>()
                userDao.update(id = userId, user = user)
                ctx.status(204)
            }

            delete("/users/{user-id}") { ctx ->
                val userId = ctx.pathParamAsClass<Int>("user-id").get()
                userDao.delete(userId)
                ctx.status(204)
            }
        }
    }.apply {
        exception(Exception::class.java) { e, ctx -> e.printStackTrace() }
        error(HttpStatus.NOT_FOUND) { ctx -> ctx.json("not found") }
    }.start(7070)

}

Conclusion

I had only worked with Kotlin for a few hours before writing this tutorial, but I’m already a very big fan of the language. Everything just seems to make sense, and the interoperability with Java is great. IntelliJ will also automatically convert Java code into Kotlin if you paste it into your project. Please clone the repo and give it a try!