What You Will Learn

  • Setting up Kotlin with Maven
  • Creating a Javalin/Kotlin CRUD REST API (no database)
  • Some neat Kotlin features

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(args: Array<String>) {
    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>1.2.0</version>
</dependency>

Not familiar with Maven? Click here for more detailed instructions.

compile 'io.javalin:javalin:1.2.0'

Not familiar with Gradle? Click here for more detailed instructions.

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

And paste the “Hello world” example:

import io.javalin.Javalin

fun main(args: Array<String>) {
    val app = Javalin.create().port(7000).start()
    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 = 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)
)

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 (so you won’t have to include guava in every project).

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:

class UserDao {

    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.put(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 }
    }

    fun update(id: Int, user: User) {
        users.put(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). We can use with (docs) and trailing closures to create very clean api declarations:

fun main(args: Array<String>) {

    val userDao = UserDao()

    val app = Javalin.create().apply {
        port(7000)
        exception(Exception::class.java) { e, ctx -> e.printStackTrace() }
        error(404) { ctx -> ctx.json("not found") }
    }.start()

    app.routes {

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

        get("/users/:id") { ctx ->
            ctx.json(userDao.findById(ctx.param("id")!!.toInt())!!)
        }

        get("/users/email/:email") { ctx ->
            ctx.json(userDao.findByEmail(ctx.param("email")!!)!!)
        }

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

        patch("/users/update/:id") { ctx ->
            val user = ctx.bodyAsClass(User::class.java)
            userDao.update(
                    id = ctx.param("id")!!.toInt(),
                    user = user
            )
            ctx.status(204)
        }

        delete("/users/delete/:id") { ctx ->
            userDao.delete(ctx.param("id")!!.toInt())
            ctx.status(204)
        }

    }

}

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!