WebFlux
wow-webflux creates command, state, snapshot, event, and query handlers from wow-openapi runtime route contracts. Use it for Wow's declarative HTTP API. Omit it when custom controllers already own the required surface.
The module owns reactive handlers, request extraction, wait policy, and error mapping. Spring WebFlux/Netty owns ports, connections, codecs, resources, and network timeouts. The application owns authentication, authorization, and gateway policy.
Installation
implementation("me.ahoo.wow:wow-spring-boot-starter") {
capabilities { requireCapability("me.ahoo.wow:webflux-support") }
}A direct wow-webflux dependency provides APIs and implementations only; it does not provide Starter's WebFluxAutoConfiguration or property binding.
Automatic Route Registration
Auto-configuration merges ordered WebFluxRouteModule instances and extra HttpRouteHandlerFunctionFactory beans, then builds one RouterFunction from RouterSpecs. Only loaded metadata and registered contributors create routes. Module presence does not prove a business endpoint exists.
Route Patterns
Aggregate and command metadata determine paths and add tenant, owner, or space scope when required. The candidate runtime /v3/api-docs is available only when the application separately installs a Springdoc WebFlux starter compatible with its Spring WebFlux version. With webflux-support alone, inspect RouterSpecs.toRouteCatalog() or an equivalent route-catalog diagnostic instead of guessing production paths.
Aggregate Route Pattern
Metadata such as @AggregateOwner(OwnerPolicy.AGGREGATE_ID) determines how owner and aggregate ID are represented. The command handler still builds a CommandMessage from path, headers, and body.
Owner Route Pattern
Owner, tenant, and space routes express resource scope only; they do not authenticate the caller. Security must bind the principal to those values and reject escalation.
HTTP Method Mapping
Methods and paths generated for @CreateAggregate and @CommandRoute come from compiler metadata; WebFlux executes the contract. Rebuild and inspect OpenAPI after annotation or metadata changes.
Configuration
The capability is sufficient for installation; wow.webflux.enabled=true by default. Common defaults are:
wow:
webflux:
global-error:
enabled: true
batch:
concurrency: 128
prefetch: 4
query:
default-list-size: 100
idle-timeout: 10s
strict-count-filter: false
query:
http:
max-list-size: 1000
max-page-size: 100
max-page-window: 10000
max-filter-nodes: 128
max-filter-values: 1000
allow-expensive-operators: trueBatch concurrency applies per request and is shared by snapshot rebuild and StateEvent resend. Concurrent requests multiply downstream load; lower it to match application and storage capacity.
0 disables each numeric HTTP guard; idle-timeout=0s disables idle timeout. Do not duplicate backend field-type, mapping, or uniqueness checks. The gateway checks the wow.query.http budget at admission for queries whose entry is HTTP, which every built-in query route writes; HttpQueryGuard keeps the HTTP adapter's own duties (row caps on responses, the limit=0 default, idle timeout, buffering). Programmatic QueryGateway calls run as in-process and are not budgeted by default; see Query Gateway.
A count body is a bare filter, and a root without op is read as a legacy condition whose operator defaults to ALL: {"field":"state.name","value":"x"} counts every row. Set strict-count-filter: true to reject such a body instead; clients then send {"op":"MATCH_ALL"} to count everything.
Aggregation Query Routes
Both Snapshot and EventStream routes accept AggregationQuery:
.../snapshot/aggregationaggregates the snapshot model;.../event/aggregationaggregates the event-stream model and follows the same base, tenant, and owner route rules as event/list, event/paged, and event/count.
Normal JSON collects dynamic rows into an array; Accept: text/event-stream streams rows. The gateway's HTTP entry budget (wow.query.http.*, checked at admission step 0) also limits conditions, values, limits, Elements, metric sorting, and expensive expressions; the route guard only caps returned rows, applies the default list size, the idle timeout and buffering.
An EventStream root filter applies to the event-stream document. After elements = [{"path":"body"}] expands the event array, group and metric fields are relative to each event item. Elasticsearch currently does not index the body.body payload, so the cross-backend aggregation scope is the event-stream envelope and body event metadata. Payload aggregation requires a separate mapping and historical reindex design first.
Command HTTP Contract
WebFlux materializes command routes and delegates requests to the command runtime. See Send Commands for routes, media types, and request construction. See Completion Semantics for wait stages, function matching, timeouts, and unknown outcomes. This page does not duplicate command headers or the wait state machine.
Error Handling
The default RequestExceptionHandler converts framework failures to ErrorInfo and writes Wow-Error-Code; a global WebExceptionHandler is enabled by default. Verified mappings include argument/state errors 400, not found 404, wait timeout 408, and unknown errors 500. A custom error strategy must preserve the original failure when response rendering is empty or fails.
One WebFluxErrorStrategy maps every error a route reports, including the ones a response carries in its body because its status is already 200: the last event of an SSE stream that fails midway, and the errorCode/errorMsg of a batch result (snapshot rebuild, StateEvent resend). Since 9.3.0 an exception the strategy does not classify is InternalServerError with the message "Unexpected server error" there too, as on JSON routes; before 9.3.0 the SSE event and the batch result said BadRequest with the exception's own message. A custom strategy that changes codes or messages overrides toErrorInfo(throwable) as well as toServerResponse, so the SSE events and batch results follow it. The reverse holds too: a strategy built by delegation (object : WebFluxErrorStrategy by DefaultWebFluxErrorStrategy) that overrides only toErrorInfo changes the SSE events and batch results but not the JSON responses, which DefaultWebFluxErrorStrategy still renders with its own mapping; override both. A custom RequestExceptionHandler reports these errors through handleInBody(request, throwable); by default it maps with DefaultWebFluxErrorStrategy and no longer calls handle.
OpenAPI Integration
Runtime metadata and route contracts assemble OpenAPI. Each instance revalidates its query schemas every wow.query.schema.revalidate-interval, and the wowQuerySchema actuator endpoint lists each instance's schema versions and revalidates on demand. The 9.1 routes POST /{aggregate}/snapshot/schema/refresh and POST /{aggregate}/event/schema/refresh remain as deprecated aliases until 10.0.0: each reloads its own model (Snapshot or EventStream) of that aggregate on the instance that answers, concurrent calls sharing the reload in flight, then returns the descriptor as GET …/schema does. Revalidation affects only the instance it runs on and keeps the previous schema when it fails; it does not broadcast or modify backend mappings. Expose the actuator endpoint only on the management plane.
Performance Optimization
Observe event-loop blocking, serialization, handler latency, open wait connections, and guard rejections before changing batch or server settings.
Reactive Processing
Built-in paths return Mono/Flux and avoid blocking in core handlers. Custom controllers must preserve that boundary. Isolate blocking cache or driver work on an appropriate scheduler and verify it under load.
Monitoring and Debugging
Record route ID, request ID, error code, HTTP status, latency, and cancellation. Add opentelemetry-support for framework spans; WebFlux capability does not configure an exporter.
Request Logging
Temporarily set me.ahoo.wow.webflux=DEBUG, but do not log command secrets, authentication headers, or complete sensitive payloads. Restore the production level after diagnosis.
Best Practices
- Verify routes from candidate runtime OpenAPI when a matching Springdoc WebFlux starter is installed; otherwise inspect the
RouterSpecsroute catalog. - Authenticate before WebFlux handlers and authorize query/command routes.
- Keep the reactive path non-blocking and test cancellation and timeout.
- Change HTTP guards from evidence; do not use
0as a default escape hatch.
Focused check:
./gradlew :wow-webflux:checkThis does not prove target gateway, security policy, or deployed routes. Next, read OpenAPI and Data access.