a lightweight web framework for Java and Kotlin

0

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

Javalin is built on top of Jetty and its performance is equivalent to raw Jetty code. Also, developers don’t need to extend a 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 a public static void main as below:

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

Let’s see an excerpt with a 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 quite simple to validate parameters such as path parameters, query parameters and form parameters 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 cool feature of Javalin that exists in other frameworks is Handlers. Javalin brings forward 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("https://www.infoq.com/", 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 AccessManagerwhere developers can implement their own access manager as they wish.

// 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 from version 3.0, Javalin also brings an OpenAPI plugin (Swagger). The full implementation of the OpenAPI 3.0 specification is available both as DSLs and annotations.

OpenAPI DSLs:

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 throw the jar with java -jar filename.jar. Javalin has an embedded Jetty server, so no application server is needed.

Javalin also has a page entirely dedicated to educatorswhere they point out that students can benefit from Javalin because there is no servlet container/application server setup required to start coding, once there is a built-in Jetty server on Javalin.

There are a series of tutorials available, such as Run on GraalVMand Kotlin CRUD REST API. The complete list can be found on the tutorial pages.

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

Share.

Comments are closed.