Spring Boot Skills
GitHub
Edge & Reactive

WebFlux Reactive Patterns

Use when the complete Spring Boot 4 request path is reactive: WebFlux controllers, Reactor pipelines, R2DBC persistence, streaming, backpressure, or cancellation. For defining and registering declarative outbound API interfaces, use http-interface-clients.

View SKILL.md

Use WebFlux when the complete request path benefits from non-blocking I/O.

Dependencies and pipeline rules

  • Use the dedicated Boot 4 WebFlux and WebFlux test starters.
  • Return Mono or Flux; never call subscribe() in request-handling code.
  • Choose flatMap, concatMap, or sequential composition according to ordering needs.
  • Keep JDBC, filesystem, and blocking SDK calls off Reactor event loops.
  • Isolate unavoidable blocking work on boundedElastic at the adapter boundary.
  • Apply timeouts to external calls and preserve cancellation.

Context and errors

  • Put request-scoped metadata in Reactor Context, not ThreadLocal.
  • Configure observability context propagation deliberately.
  • Translate domain failures centrally without swallowing cancellation.
  • Use onErrorResume only for a defined fallback and avoid onErrorContinue.

Persistence and streaming

  • Use R2DBC for reactive database access.
  • Bound fan-out concurrency according to downstream capacity.
  • Define backpressure, buffering, and maximum in-memory sizes.
  • Never collect an unbounded stream solely to simplify downstream code.

Testing

  • Use StepVerifier for publisher behavior and WebTestClient for contracts.
  • Test cancellation, timeout, empty results, errors, ordering, and backpressure.
  • Detect blocking calls in tests when practical.

Examples

  • See examples/good-reactive-service.java and examples/bad-reactive-service.java.

Official sources

Gotchas

  • Agent adds MVC and WebFlux starters accidentally - choose the intended web stack.
  • Agent calls block() or subscribe() in application flow - keep subscription with the runtime.
  • Agent uses ThreadLocal for tenant or trace data - use Reactor Context.
  • Agent uses unbounded flatMap - set concurrency from downstream limits.
  • Agent wraps JDBC with Mono.just - this still blocks the event loop.
FROM THE REPOSITORY

Examples & templates

bad-reactive-service.javaexamples
Source on GitHub
Mono<Order> loadOrder(UUID id) {
    Order order = repository.findById(id).block();
    return Mono.just(order);
}
good-reactive-service.javaexamples
Source on GitHub
import java.time.Duration;
import java.util.UUID;

import reactor.core.publisher.Mono;

final class OrderQueryService {

    private final ReactiveOrderRepository repository;

    OrderQueryService(ReactiveOrderRepository repository) {
        this.repository = repository;
    }

    Mono<Order> loadOrder(UUID id) {
        return repository.findById(id)
            .switchIfEmpty(Mono.error(new OrderNotFound(id)))
            .timeout(Duration.ofSeconds(2));
    }
}

interface ReactiveOrderRepository {
    Mono<Order> findById(UUID id);
}

record Order(UUID id) { }

final class OrderNotFound extends RuntimeException {
    OrderNotFound(UUID id) {
        super("Order not found: " + id);
    }
}

Verification scope

Compiled sources
No
Runtime checks
No

Coverage describes checked-in fixtures, not a guarantee for every snippet or project.

Coverage matrix & agent evidence
ADD TO YOUR AGENT

Install Boot 4 skills

claude plugin marketplace add rrezartprebreza/spring-boot-skills
claude plugin install spring-boot-4-skills@spring-boot-skills