Vert.x Web Routing
Vert.x Web router, sub-routers, body handler, validation, and error handling.
# CLAUDE.md — Vert.x Web Routing
## Setup
- Add `io.vertx:vertx-web` for routing on top of Vert.x core.
- Build a `Router`:
```java
Router router = Router.router(vertx);
router.get("/users/:id").handler(this::getUser);
router.post("/users").handler(BodyHandler.create()).handler(this::createUser);
vertx.createHttpServer().requestHandler(router).listen(8080);
```
- One router per HTTP server. For multi-tenancy or sub-app patterns, use sub-routers (`Router.router(vertx)`) and mount with `router.route("/api").subRouter(apiRouter)`.
## Handlers
- Each handler takes a `RoutingContext`. Read params, body, headers; write response.
- Always end the response: `ctx.json(dto)` or `ctx.response().end()`. Forgetting hangs the client.
- Chain handlers with `.handler(...)`. Each can short-circuit by ending the response or throwing.
## Body parsing
- `BodyHandler.create()` must come before any handler that reads the body. Mount it once at the top:
```java
router.route().handler(BodyHandler.create());
```
- For large bodies, set `setBodyLimit(maxBytes)`.
- JSON: `ctx.body().asJsonObject()` or `ctx.body().asPojo(MyDto.class)` (with the right codec).
## Path & query params
- `ctx.pathParam("id")` for path params.
- `ctx.queryParam("page")` returns `List<String>` — multi-value aware.
- For typed binding, use **OpenAPI Router Builder** (`OpenAPIContract.from(...)`) to generate routers from a spec with auto-validation.
## Validation
- Use `vertx-web-validation` for declarative request validation:
```java
ValidationHandler validation = ValidationHandlerBuilder.create(schemaParser)
.pathParameter(Parameters.param("id", Schemas.stringSchema()))
.body(Bodies.json(MY_SCHEMA))
.build();
router.post("/users").handler(validation).handler(this::createUser);
```
- Errors reach a final error handler — translate to JSON.
## Error handling
- `router.errorHandler(500, ctx -> { ... })` for centralized error responses.
- `ctx.fail(statusCode, throwable)` from inside a handler to escalate to the error handler.
- Don't leak stack traces in production. Map to a clean error envelope.
## CORS
- `CorsHandler.create("https://app.example.com").allowedMethod(...)`.
- Mount before route handlers.
- Don't use `*` origin with credentials. The browser will refuse.
## Auth
- `AuthenticationHandler` (e.g., `JWTAuthHandler`) protects routes:
```java
router.route("/admin/*").handler(JWTAuthHandler.create(jwtAuth));
```
- Authorization (role checks) via `AuthorizationProvider` + `AuthorizationHandler`.
## Static files
- `StaticHandler.create()` serves files from the classpath or filesystem.
- For SPAs: route `/api/*` to handlers, fallback to `StaticHandler.create("dist").setIndexPage("index.html")`.
## Don't
- Don't put the body handler after the route handler that reads the body — body will be empty.
- Don't write blocking code in route handlers. Use `Future`, Mutiny `Uni`, or `executeBlocking`.
- Don't write to the response after calling `end()`. Vert.x will throw or silently drop.
- Don't use one giant `MainVerticle` with hundreds of routes. Split by feature into multiple verticles or routers.
Other Java templates
Modern Java Rules
Java 21+ defaults: records, sealed types, pattern matching, virtual threads, var.
Java Testing with JUnit 5
JUnit 5 + AssertJ + Mockito + Testcontainers — opinionated test conventions.
Java Virtual Threads & Concurrency
Project Loom virtual threads, structured concurrency, and modern concurrent patterns.
Java Build (Maven & Gradle)
Build conventions, dependency management, multi-module projects, and CI patterns.
Java Streams & Collections
Streams API, immutable collections, Collectors, and idiomatic data manipulation.