Minimal Java web framework. Anti-Spring.
File path = URL path. Method name = HTTP verb.
# Java 21 required
brew install openjdk@21
# Enable git hooks (formatting + conventional commits)
git config core.hooksPath .githooks
# Verify
./gradlew buildOptional - For format-on-save in Zed, install google-java-format:
brew install google-java-formatThen add to Zed/VScode settings:
{
"languages": {
"Java": {
"formatter": {
"external": {
"command": "google-java-format",
"arguments": ["--aosp", "-"]
}
}
}
}
}Run the example app:
./gradlew runExample routes live in examples/basic/routes/.
Smoke test:
./scripts/smoke.shDev helpers:
./scripts/watch.sh(requireswatchexecorentr)./scripts/clean-cache.sh(clears the route compiler cache)./scripts/new-route.sh /users/[id](scaffolds a new route)
Current status/backlog: docs/STATUS.md.
routes/index.java→GET /routes/users/index.java→GET /usersroutes/users/[id].javaorroutes/users/[id]/index.java→GET /users/:idviactx.param("id")- Each file defines
public class Route - Each handler is
public Object get(Ctx ctx)/post/put/delete/patch - Return value:
String→text/plainRes→ status/headers + body- anything else → JSON
Ctx helpers:
ctx.param(name),ctx.query(name),ctx.queryAll(name)ctx.header(name),ctx.headers(name)ctx.cookie(name)
Register global middleware when starting:
Winter.start(config, new MyMiddleware());Middleware hooks (winter.middleware.Middleware):
before(ctx)can short-circuit by returning a response object (oftenRes)after(ctx, result)can wrap/transform the response (e.g. add headers)onError(ctx, exception)can handle exceptions and return a response
HEADfalls back toGET(same status/headers, no body)OPTIONSreturns204and includes anAllowheader for matched routes
WinterConfig.DEFAULT_MAX_BODY_BYTESis enforced for request bodies (413 on overflow)- Set
WinterConfig.withExposeErrors(true)to include exception messages in 500s (default: false) - Set
WinterConfig.withHotReload(true)to watchroutes/and recompile on change (default: false)