Developing web applications with Javalin and Kotlin

This article will walk you through how to develop web applications in Kotlin using Javalin. Javalin and Kotlin work well together, as they are both very pragmatic and focus on getting things done quickly and with a small amount of code. We will start with a simple “Hello World” example, then look at server configuration, application structure, and finally deployment.

The code examples in this article are available on GitHub.

Hello World

To create a simple server which responds with 

Hello World

 for a 

GET

 request to 

/

, we need two lines of Javalin. One line to create and start the server, and one line to add a 

Handler

 to a path.

import io.javalin.Javalin

fun main(args: Array<String>) {
    val app = Javalin.start(7000) // create and start a server
    app.get("/") { ctx -> ctx.result("Hello World") } // add a handler to `/`
}

This starts a basic Javalin server with the default configuration. Next we’ll look at how we can configure this server to fit our needs.

Configuring the server

Javalin’s configuration options are all available on the main

Javalin

class through a fluent API. A full overview of all options is available in the docs.

In the following sections we will set a custom Jetty server and enable logging.

Custom EmbeddedServer

Javalin runs on an embedded Jetty server. You can configure this server in whatever way you want, and Javalin will add itself to it without ruining your configuration. As an example of a custom server we will use a custom

ThreadPool

and

Connector

-timeout settings:

val app = Javalin.create().apply {
    embeddedServer(EmbeddedJettyFactory {
        Server(QueuedThreadPool(200, 8, 120_000)).apply {
            connectors = arrayOf(ServerConnector(server).apply {
                port = 7070
                idleTimeout = 120_000
            })
        }
    })
}.start()

Even if the Jetty Server is a Java API, we can use

apply

to make the configuration elegant. Using a custom Jetty server is usually not necessary, but it’s required if you want to use Jetty for SSL or HTTP2.

Logging

To add logging we can use

app.requestLogLevel(logLevel)

. The available levels are

OFF

,

STANDARD

and

EXTENSIVE

. For development, the

EXTENSIVE

level can be very useful, since it logs the full request and response. Keep in mind that this has a considerable performance impact, so don’t use it in production. Let’s add it to the snippet above:

val app = Javalin.create().apply {
    embeddedServer(EmbeddedJettyFactory {
        Server(QueuedThreadPool(200, 8, 120_000)).apply {
            connectors = arrayOf(ServerConnector(server).apply {
                port = 7070
                idleTimeout = 120_000
            })
        }
    })
    requestLogLevel(LogLevel.EXTENSIVE) // this has been added
}.start()

Setting up routes and controllers

Javalin apps are built around

Handler

lambdas. You have:

  • before-handlers,
  • endpoint-handlers,
  • after-handlers,
  • exception-handlers
  • and error-handlers.

In other web frameworks an endpoint-handler is sometimes called a “route”, and before/after handlers are called “filters” or “middleware”.

Let’s take a closer look at the different handlers.

Endpoint handlers

The main handler-type in Javalin is the endpoint-handler. The endpoint-handler is attached to the Javalin server by calling the appropriate verb-method.

The

Handler

interface looks like this:

@FunctionalInterface
public interface Handler {
    void handle(Context ctx) throws Exception;
}

And the signature of the verb-methods are

verb(String path, Handler handler)

This allows you to use lambdas in trailing closures:

app.get("/") { ctx -> 
    ctx.result("Hello World"))
}

This is great for small services, but as your service grows you have to create controllers. You can structure your application by using the

ApiBuilder

class and method references:

app.routes {
    path("users") {
        get(UserController::getAllUsers);
        post(UserController::createUser);
        path(":id") {
            get(UserController::getUser);
            patch(UserController::updateUser);
            delete(UserController::deleteUser);
        }
    }
}

The example shows calling method references directly on the object/class, but you can also create an instance of

userController

if you want to do dependency injection.

An implementation of one of these methods would look like this:

object UserController {
    function getAllUsers(ctx: Context) {
        ctx.json(DummyDao.findAllUsers())
    }
}

We are using

ctx.json()

here to transform a list of users into a JSON object. Javalin relies on Jackson to perform this serialization, which needs to be added to your

build.gradle

/

pom.xml

.

The

Context

object.

We’ve seen the

Context

object (ctx) a few times so far, but we haven’t learned what it is yet. The

Context

object is a wrapper for

HttpServletRequest

and

HttpServletResponse

. It contains everything you need to handle a HTTP-request.

  • If you need to extract a query-param, you can call
    ctx.queryParam("qp")

    .

  • If you need to get all the headers, you can do
    ctx.headerMap()

    .

  • To set a result you can do
    ctx.result("res")

    .

A full list of all the available options can be found in the docs.

As a general rule, anything that “sets” something on the context operates on the response, and anything that “gets” something operates on the request.

Before/after handlers

Sometimes you need to do setup/cleanup before/after processing a request. You use before/after handlers for this in Javalin. They function like

@Before

and

@After

methods in JUnit, and are run before and after the endpoint handlers.

app.before { ctx -> 
    // do something before every request
}
app.get("/test") { ctx ->
    // do something for GET to /test endpoint
}
app.after("/test") { ctx -> 
    // do something after requests to /test
}

Exception handlers

As you saw earlier, the

Handler

interface throws

Exception

. All exceptions are caught by the

ExceptionMapper

. There is a special exception class called HaltException which is handled before other exceptions. To handle any sort of exception, you add an exception-handler similarily to the endpoint-handlers and before/after handlers:

app.exception(NullPointerException.class, (e, ctx) -> {
    // handle exception here
});

If you have mulitple handlers capable of handling an exception (ex:

NullPointerException

,

RuntimeException

and

Exception

), the most specific one will be used.

Error handlers

Often you’ll want to handle exceptions differently, but you want them all to result in the same error message. For example, the exceptions from the previous section should probably all turn into a

500 - Internal server error

message. This can be solved by making all the exception-handlers set the status to 500, and adding an error-handler. This will also allow you to point to the same error-message from your endpoint-handlers:

app.get("/error") { ctx ->
    if (somethingBad) {
        ctx.status(500)
    }
    ...
}

app.exception(NullPointerException::class.java, { e, ctx ->
    // do something
    ctx.status(500)
}).exception(RuntimeException::class.java, { e, ctx ->
    // do something
    ctx.status(500)
})

app.error(500) { ctx ->
    ctx.result("500 - Internal server error") // all 500s end up like this
}

Going async

Javalin is intended to be very simple for beginners to get started with, but it also wants to offer advanced functionality for power users. To run your requests asynchronously, simply pass a 

CompletableFuture

 to 

ctx.result()

:

app.get("/") { ctx -> ctx.result(getFuture()) }

// hopefully your future is less pointless than this:
private fun getFuture() = CompletableFuture().apply {
    Executors.newSingleThreadScheduledExecutor().schedule({ this.complete("Hello World!") }, 1, TimeUnit.SECONDS)
}

Deployment

To deploy a Javalin app you just have to package it as a jar and launch it.

Building a jar

There is nothing Javalin specific about this, we just need to create a jar with all the dependencies and launch it.

Here one example for Gradle and one for Maven:

Gradle: https://github.com/johnrengelman/shadow
Maven: https://maven.apache.org/plugins/maven-shade-plugin

Then simply launch the jar with

java -jar filename.jar

. That’s it!

Conclusion

Now you know a little about how to use Javalin in Kotlin. Some Kotlin features were particularly useful, for example

apply

when working towards Jetty’s Java API, or the trailing closures which are used extensively with all Javalin handlers.

If you’re interested in learning more about web development using Kotlin and Javalin, I have written a lot of other tutorials on it. Here are five popular ones:

David Åse is the creator of Javalin, a JVM web-framework for Kotlin and Java. He currently works as a Software Engineer at Working Group Two, a startup looking to disrupt the telecom industry. He previously worked for Telenor, a major global telecom.

You can find him on LinkedIn. David doesn’t have twitter, but he would appreciate it if you followed Javalin’s Twitter.

The post Developing web applications with Javalin and Kotlin appeared first on SuperKotlin.