Skip to content

Handling web requests

Fluxzero supports first-class WebRequest handling via the @HandleWeb annotation and its HTTP-specific variants such as @HandleGet, @HandlePost, @HandleDelete, etc.

Instead of exposing a public HTTP server per application, Fluxzero uses a central Web Gateway that proxies all external HTTP(S) and WebSocket traffic into the Runtime as WebRequest messages. These messages are:

  • Logged for traceability and auditing
  • Routed to client applications using the same handler system as for commands, events, and queries
  • Handled by consumer applications which return a WebResponse

This architecture enables several key benefits:

  • Zero exposure: client apps do not require a public-facing HTTP server and are thus invisible to attackers
  • Back-pressure support: applications control load by polling their own messages
  • Audit-friendly: every incoming request is automatically logged and correlated to its response
  • Multiple consumers possible: multiple handlers can react to a WebRequest, though typically only one produces the response (others use passive = true)
@HandleGet("/users")
public List<UserAccount> listUsers() {
return userService.getAllUsers();
}

This will match incoming GET /users requests and return a list of users. The response is published as a WebResponse.

Use the general-purpose @HandleWeb annotation if you want to support multiple paths, multiple methods, or define a custom method:

@HandleWeb(value = "/users/{userId}", method = {"GET", "DELETE"})
public CompletableFuture<?> handleUserRequest(WebRequest request, @PathParam String userId) {
return switch (request.getMethod()) {
case "GET" -> Fluxzero.query(new GetUser(userId));
case "DELETE" -> Fluxzero.sendCommand(new DeleteUser(userId));
default -> throw new UnsupportedOperationException();
};
}
@HandleWeb(value = "/users/{userId}", method = HttpRequestMethod.ANY)
public CompletableFuture<?> handleAllUserMethods(WebRequest request, @PathParam String userId) {
// Handle any method (GET, POST, DELETE, etc.)
...
}

If you want to listen to a WebRequest without returning a response—e.g. for monitoring or audit logging—use passive = true:

@HandlePost(value = "/log", passive = true)
public void log(WebRequest request) {
logService.store(request);
}

Use @PathParam to extract dynamic segments from the URI:

@HandleGet("/users/{id}")
public UserAccount getUser(@PathParam String id) {
return userService.get(id);
}

If no name is provided to @PathParam, the parameter name is used by default.

You can also use these to extract other parts of the request:

  • @QueryParam – extract query string values
  • @HeaderParam – extract headers
  • @CookieParam – extract cookies
  • @FormParam – extract form-encoded values (e.g., from POST body)

Use @Path on packages, classes, methods, and properties to construct URI paths. Paths are composed from outermost to innermost.

  • A path starting with / resets the chain
  • Empty paths use the simple name of the enclosing package or class
  • If applied to a property (field or getter), its value is used as a dynamic segment
@Path
package my.example.api;
@Path("users")
public class UserHandler {
@Path("{id}")
@HandleGet
public User getUser(@PathParam String id) { ... }
}

This matches /api/users/{id}. If @Path("/users") had been used on the class instead, it would reset the prefix and match /users/{id}.


Use @ServeStatic to serve static content directly from your app without an external web server:

@ServeStatic(value = "/web", resourcePath = "/static")
public class WebAssets {
}

This serves files from /static (in classpath or filesystem) under the URI path /web/**.

  • Supports classpath and file system resources
  • Optional fallback file (e.g. for SPAs)
  • Clean URLs for statically exported routes
  • Smart Cache-Control headers
  • GZIP and Brotli compression (if precompressed assets are present)
@ServeStatic(
value = "/assets",
resourcePath = "/public",
fallbackFile = "index.html",
cleanUrls = true,
immutableCandidateExtensions = {"js", "css", "svg"},
maxAgeSeconds = 86400
)
  • value: Web URI path(s) to expose
  • resourcePath: Filesystem or classpath root
  • fallbackFile: Fallback for unknown paths (e.g. index.html)
  • cleanUrls: Whether extensionless paths first try <path>.html and <path>/index.html before the fallback
  • immutableCandidateExtensions: Enables long-lived caching for fingerprinted assets
  • maxAgeSeconds: Cache duration for other resources
@ServeStatic(value = "/app", resourcePath = "/static", fallbackFile = "index.html")
public class WebFrontend {
}

This serves:

  • /app/index.html, /app/main.js, etc. from /static
  • /app/onboarding from /static/onboarding.html or /static/onboarding/index.html when clean URLs are enabled
  • Falls back to index.html for client-side routing

Set cleanUrls = false if extensionless paths should only try the exact path before the fallback.

To restrict resolution:

  • Use classpath:/... to only serve classpath files
  • Use file:/... to only serve local files

You can mix @ServeStatic with dynamic handlers:

@Path("/app")
@ServeStatic("static")
public class AppController {
@HandleGet("/status")
public Status getStatus() {
return new Status("OK", Instant.now());
}
@HandlePost("/submit")
public SubmissionResult submitForm(FormData data) {
return formService.handle(data);
}
}

This will:

  • Serve /app/static/** from the static/ directory
  • Also handle /app/status and /app/submit dynamically

© 2026 Fluxzero