This page boots a Chappe HTTP server in under 30 lines of Java, then walks through a typed handler, static file serving, and a minimal wrk load test.
Prerequisites
-
Java 25 — Temurin recommended.
-
Maven 3.9.16 — pinned via
.sdkmanrcat the root of thechapperepo. -
sdk envfrom the repo root loads the right JDK and Maven. Ifmvnfails withmodelVersion 4.1.0 not supported, switch back to Maven 4:sdk use maven 3.9.16. -
Optional:
wrkfor the final load test (brew install wrkor equivalent).
Maven coordinates
<dependency>
<groupId>io.vidocq.chappe</groupId>
<artifactId>chappe-api</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.vidocq.chappe</groupId>
<artifactId>chappe-core</artifactId>
<version>0.1.0-SNAPSHOT</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.vidocq.chappe</groupId>
<artifactId>chappe-http</artifactId>
<version>0.1.0-SNAPSHOT</version>
<scope>runtime</scope>
</dependency>
chappe-api is the public API, compiled at compile scope. chappe-core (server engine) and chappe-http (HTTP/1.1 and HTTP/2 protocols) sit at runtime scope, discovered via ServiceLoader (ServerProvider).
Hello world
import io.vidocq.chappe.api.*;
void main() {
var router = Router.builder()
.get("/", _ -> Response.ok("Hello, Chappe!"))
.get("/users/{id}", req -> Response.ok("User " + req.pathParams().get("id")))
.build();
try (var server = Server.builder()
.port(8080)
.handler(router)
.build()) {
server.start();
Thread.currentThread().join();
}
}
Server is AutoCloseable; start() opens the listening socket and spawns the accept loop on a virtual thread.
|
|
First typed handler
To read a JSON body, write a POST, and return an explicit status:
import io.vidocq.chappe.api.*;
import java.nio.charset.StandardCharsets;
Router api = Router.builder()
.post("/users", req -> {
var body = new String(
req.body().asInputStream().readAllBytes(),
StandardCharsets.UTF_8
);
return Response.builder()
.status(StatusCode.CREATED)
.header("Content-Type", "application/json")
.body("{\"received\": " + body + "}")
.build();
})
.delete("/users/{id}", req -> Response.of(StatusCode.NO_CONTENT))
.build();
Serving a static directory
var staticHandler = StaticFileHandler.builder()
.addPath(java.nio.file.Path.of("./public"))
.addClasspath("static")
.cacheInMemory(true)
.cacheControl("max-age=3600, public")
.build();
Server.builder().port(8080).handler(staticHandler).build().start();
StaticFileHandler auto-detects MIME (26+ types), answers 304 Not Modified on If-Modified-Since or If-None-Match, and blocks path traversal.
Build and run
sdk env # loads JDK 25 + Maven 3.9.16
./mvnw -ntp install -DskipTests
java \
--module-path target/lib:target/myapp.jar \
--module myapp/io.example.Main