Important: This news article covers an old version of Javalin (v1.0.0). The current version is v6.1.3.
See the documentation page for up-to-date information.

Introducing Javalin

Javalin is a very lightweight web framework for Kotlin and Java, inspired by Sparkjava and koa.js. Javalin is written in Kotlin with a few functional interfaces written in Java. This was necessary to provide an enjoyable and near identical experience for both Kotlin and Java developers.

Javalin is really more library than framework; you don’t need to extend or implement anything and there are very few “Javalin-concepts” you have to learn. Let’s look at some examples:

Hello World

  • Kotlin
  • Java
fun main(args: Array<String>) {
    val app = Javalin.start(7000)
    app.get("/") { ctx -> ctx.result("Hello World") }
}
public static void main(String[] args) {
    Javalin app = Javalin.start(7000);
    app.get("/", ctx -> ctx.result("Hello World"));
}

API structure and server config

  • Kotlin
  • Java
val app = Javalin.create().apply {
    enableStaticFiles("/public")
    enableStandardRequestLogging()
    port(port)
}.start()

app.routes {
    path("users") {
        get(UserController::getAllUserIds)
        post(UserController::createUser)
        path(":user-id") {
            get(UserController::getUser)
            patch(UserController::updateUser)
            delete(UserController::deleteUser)
        }
    }
}
Javalin app = Javalin.create()
    .enableStaticFiles("/public")
    .enableStandardRequestLogging()
    .port(port)
    .start();

app.routes(() -> {
    path("users", () -> {
        get(UserController::getAllUserIds);
        post(UserController::createUser);
        path(":user-id"(() -> {
            get(UserController::getUser);
            patch(UserController::updateUser);
            delete(UserController::deleteUser);
        });
    });
});

Filters and mappers

  • Kotlin
  • Java
app.before("/some-path/*") { ctx ->  ... } // runs before requests to /some-path/*
app.before { ctx -> ... } // runs before all requests
app.after { ctx -> ... } // runs after all requests
app.exception(Exception.class) { e, ctx -> ... } // runs if uncaught Exception
app.error(404) { ctx -> ... } // runs if status is 404 (after all other handlers)
app.before("/some-path/*", ctx -> { ... }); // runs before requests to /some-path/*
app.before(ctx -> { ... }); // runs before all requests
app.after(ctx -> { ... }); // runs after all requests
app.exception(Exception.class, (e, ctx) -> { ... }); // runs if uncaught Exception
app.error(404, ctx -> { ... }); // runs if status is 404 (after all other handlers)

WebSockets

  • Kotlin
  • Java
app.ws("/websocket") { ws ->
    ws.onConnect { session -> println("Connected") }
    ws.onMessage { session, message ->
        println("Received: " + message)
        session.remote.sendString("Echo: " + message)
    }
    ws.onClose { session, statusCode, reason -> println("Closed") }
    ws.onError { session, throwable -> println("Errored") }
}
app.ws("/websocket", ws -> {
    ws.onConnect(session -> System.out.println("Connected"));
    ws.onMessage((session, message) -> {
        System.out.println("Received: " + message);
        session.getRemote().sendString("Echo: " + message);
    });
    ws.onClose((session, statusCode, reason) -> System.out.println("Closed"));
    ws.onError((session, throwable) -> System.out.println("Errored"));
});

Object mapping

  • Kotlin
  • Java
var todos = arrayOf(...)
app.get("/todos") { ctx -> // map array of Todos to json-string
    ctx.json(todos)
}
app.put("/todos") { ctx -> // map request-body (json) to array of Todos
    todos = ctx.bodyAsClass(Array<Todo>::class.java)
    ctx.status(204)
}
Todo[] todos = ...
app.get("/todos", ctx -> { // map array of Todos to json-string
    ctx.json(todos);
});
app.put("/todos", ctx -> { // map request-body (json) to array of Todos
    todos = ctx.bodyAsClass(Todo[].class);
    ctx.status(204);
});

Uploads

  • Kotlin
  • Java
app.post("/upload") { ctx ->
    ctx.uploadedFiles("files").forEach { (contentType, content, name, extension) ->
        FileUtils.copyInputStreamToFile(content, File("upload/" + name))
    }
}
app.post("/upload", ctx -> {
    ctx.uploadedFiles("files").forEach(file -> {
        copyInputStreamToFile(file.getContent(), new File("upload/" + file.getName()));
    });
});

Kotlin and Java interoperability

The interoperability between Kotlin and Java is very important to Javalin. When moving a Javalin project from Java to Kotlin, you shouldn’t need to learn a new way of doing things. In the above examples you can switch between Kotlin and Java and see that everything is more or less the same, except for a few parens and semicolons. To maintain this consistent API for both languages is an important goal of the project.

Main features

  • Lightweight - You don’t have to learn any advanced concepts to get started
  • Consistent API - All handlers and mappers are void and operate on the Context (ctx)
  • Almost identical API for both Kotlin and Java
  • Not opinionated - A library rather than a framework (you never have to extend anything)
  • Fully customizable embedded server (Jetty)
  • JSON-object-mapping (via Jackson)
  • Simple per-endpoint auth via AccessManager interface
  • Simple static file handling
  • Lifecycle events
  • CookieStore - An easy way to serialize and store objects in cookies
  • Template rendering (Velocity, Freemarker, Thymeleaf, Mustache)
  • Markdown rendering

Get involved

If you want to contribute to the project, head on over to GitHub.

No breaking changes since 0.5.0

If you’re currently using a 0.5.X version of Javalin, upgrading to 1.0.0 should not break anything.