BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News Introducing Javalin: a Lightweight Web Framework for Java and Kotlin

Introducing Javalin: a Lightweight Web Framework for Java and Kotlin

Leia em Português

This item in japanese

Javalin is a lightweight web framework for Kotlin and Java designed to be simple and blocking by default, supporting WebSockets, HTTP2, and async requests. Javalin started as a fork of the SparkJava framework, but quickly turned into a ground-up rewrite influenced by the JavaScript framework koa.js.

Javalin is built on top of Jetty, and its performance is equivalent to raw Jetty code. Furthermore, developers do not need to extend any class, use @Annotations, or even download a different version of Javalin for Java and another for Kotlin.

To get started with Javalin using Java, developers only need to a public static void main as below:

public static void main(String[] args) {
    var app = Javalin.create().start(7000);
    app.get("/", ctx -> ctx.result("Hello World"));
}

Let's see a snippet with some configuration:

var app = Javalin.create(config -> {
    config.defaultContentType = "application/json";
    config.autogenerateEtags = true;
    config.addStaticFiles("/public");
    config.asyncRequestTimeout = 10_000L;
    config.dynamicGzip = true;
    config.enforceSsl = true;
}).routes(() -> {
    path("users", () -> {
        get(UserController::getAll);
        post(UserController::create);
        path(":user-id"(() -> {
            get(UserController::getOne);
            patch(UserController::update);
            delete(UserController::delete);
        });
        ws("events", userController::webSocketEvents);
    });
}).start(port);

It is pretty simple to validate parameters such as path params, query params, and form params on Javalin:

var myQpStr = ctx.queryParam("my-qp"); // no validation, returns String or null
var myQpInt = ctx.pathParam("my-qp", Integer.class).get(); // returns an Integer or throws
var myQpInt = ctx.formParam("my-qp", Integer.class).check(i -> i > 4).get(); // Integer > 4

// validate two dependent query parameters:
var fromDate = ctx.queryParam("from", Instant.class).get();
var toDate = ctx.queryParam("to", Instant.class)
        .check(it -> it.isAfter(fromDate), "'to' has to be after 'from'")
        .get();

// validate a json body:
var myObject = ctx.bodyValidator(MyObject.class)
        .check(obj -> obj.myObjectProperty == someValue)
        .get();

Another interesting feature of Javalin that exists in other frameworks, are Handlers. Javalin brings before-handlers, endpoint-handlers, after-handlers, exception-handlers and error-handlers.

//before handlers
app.before(ctx -> {
    // runs before all requests
});
app.before("/path/*", ctx -> {
    // runs before request to /path/*
});

//endpoint handlers
app.get("/", ctx -> {
    // some code
    ctx.json(object);
});

app.get("/hello/*, ctx -> {
    // capture all request to sub-paths of /hello/
});

//after handlers
app.after(ctx -> {
    // run after all requests
});
app.after("/path/*", ctx -> {
    // runs after request to /path/*
});

To handle authentication/authorization, Javalin brings the functional interface AccessManager, where developers can implement their own access-manager however they want.

// Set the access-manager that Javalin should use:
app.accessManager((handler, ctx, permittedRoles) -> {
    MyRole userRole = getUserRole(ctx);
    if (permittedRoles.contains(userRole)) {
        handler.handle(ctx);
    } else {
        ctx.status(401).result("Unauthorized");
    }
});

Role getUserRole(Context ctx) {
    // determine user role based on request
    // typically done by inspecting headers
}

enum MyRole implements Role {
    ANYONE, ROLE_ONE, ROLE_TWO, ROLE_THREE;
}

app.routes(() -> {
    get("/un-secured",   ctx -> ctx.result("Hello"),   roles(ANYONE));
    get("/secured",      ctx -> ctx.result("Hello"),   roles(ROLE_ONE));
});

Starting with version 3.0, Javalin also brings an OpenAPI (Swagger) plugin. The full implementation of the OpenAPI 3.0 specification is available both as a DSL and as annotations.

OpenAPI DSL:

val addUserDocs = document()
        .body()
        .result("400")
        .result("204")

fun addUserHandler(ctx: Context) {
    val user = ctx.body()
    UserRepository.addUser(user)
    ctx.status(204)
}

OpenAPI annotations:

@OpenApi(
    requestBody = OpenApiRequestBody(User::class),
    responses = [
        OpenApiResponse("400", Unit::class),
        OpenApiResponse("201", Unit::class)
    ]
)
fun addUserHandler(ctx: Context) {
    val user = ctx.body()
    UserRepository.createUser(user)
    ctx.status(201)
}

To deploy a Javalin application, developers just need to create a jar with dependencies (using maven-assembly-plugin), then launch the jar with java -jar filename.jar. Javalin has a Jetty embedded server, so no application server is necessary.

Javalin also has a page dedicated entirely to educators, where they emphasize that students can benefit from Javalin as there is no Servlet Container/Application Server configuration required for start coding , once there is an embedded Jetty server on Javalin.

There is a series of tutorials available, such as Running on GraalVM, and Kotlin CRUD REST API. The complete list can be found on the tutorials page.

More details on Javalin can be found on the documentation page. Users can download Javalin via maven or manually from maven central.

Rate this Article

Adoption
Style

BT