Developing web applications with Javalin and Kotlin

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<String>().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.

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *