Getting Started with Javalin: A Lightweight Java Web Framework
This article introduces Javalin, a lightweight Java web framework that supports HTTP/2, WebSocket, and asynchronous requests, demonstrates a practical code example with routing, request validation, handlers, and deployment steps, and even solves a common port‑conflict issue on macOS.
Javalin is a lightweight Java web framework that originated as a fork of SparkJava and draws inspiration from the JavaScript framework koa.js. It supports WebSocket, HTTP/2, and asynchronous requests, allowing developers to start an HTTP service with just a few lines of code.
Below is a slightly more complex example that creates a Javalin application, configures default content type, static files, async timeout, gzip, SSL enforcement, and defines RESTful routes for user management, including WebSocket events.
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);The framework provides convenient methods for validating query, path, and form parameters, as well as body payloads. Examples show how to retrieve a string query parameter, cast a path parameter to an integer, enforce numeric ranges, and validate JSON bodies against custom rules.
var myQpStr = ctx.queryParam("my-qp"); // returns String or null
var myQpInt = ctx.pathParam("my-qp", Integer.class).get(); // returns int or throws
var myQpInt = ctx.formParam("my-qp", Integer.class).check(i -> i > 4).get(); // int > 4
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();
var myObject = ctx.bodyValidator(MyObject.class)
.check(obj -> obj.myObjectProperty == someValue)
.get();Javalin also allows the definition of global and route‑specific handlers using before and after hooks, as well as endpoint handlers for GET, POST, etc. Access control can be implemented via the AccessManager interface.
// Global before handler
app.before(ctx -> {
// runs before every request
});
app.before("/path/*", ctx -> {
// runs before requests matching /path/*
});
app.get("/", ctx -> {
ctx.json(object);
});
app.after(ctx -> {
// runs after every request
});To deploy a Javalin application, developers can package the project into a single JAR (using the Maven assembly plugin) and run it with java -jar app.jar . Javalin embeds Jetty, so no external servlet container is required.
The article also describes a practical troubleshooting scenario where the default port 7000 was already occupied on macOS by the system ControlCenter app. Using lsof and ps commands, the offending process was identified and disabled, allowing Javalin to start successfully.
❯ sudo lsof -nP -i4TCP | grep 7000
... TCP *:7000 (LISTEN)
❯ sudo ps aux | grep 1578
... /System/Library/CoreServices/ControlCenter.app/Contents/MacOS/ControlCenterOverall, the article serves as a concise tutorial for developers interested in quickly building and deploying Java web services with Javalin.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.