Spring Boot Skills
GitHub
Edge & Reactive

WebFlux Reactive Patterns

Use when the complete Spring Boot 3 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 can benefit from non-blocking I/O.

Pipeline rules

  • Return Mono or Flux; do not call subscribe() in request-handling code.
  • Compose work with map, flatMap, concatMap, and zip according to ordering requirements.
  • Keep blocking JDBC, filesystem, and legacy SDK calls out of event-loop threads.
  • Isolate unavoidable blocking calls on boundedElastic at the narrow adapter boundary.
  • Apply timeouts at remote-call boundaries and preserve cancellation.

Context and errors

  • Put request-scoped metadata in Reactor Context, not ThreadLocal.
  • Translate domain failures centrally without swallowing cancellation or infrastructure errors.
  • Use onErrorResume only when a defined fallback exists.
  • Avoid onErrorContinue; it makes partial processing hard to reason about.

Persistence and streaming

  • Use R2DBC for reactive database access; JDBC makes the pipeline blocking.
  • Bound concurrency for fan-out operations.
  • Define backpressure and buffering limits for streaming endpoints.
  • Avoid collecting an unbounded Flux into memory.

Testing

  • Use StepVerifier for publisher behavior and WebTestClient for HTTP contracts.
  • Test cancellation, timeout, empty results, errors, ordering, and backpressure.
  • Enable blocking-call detection in tests when the project supports it.

Examples

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

Gotchas

  • Agent calls block() in a controller or service - keep the full request path non-blocking.
  • Agent calls subscribe() manually - the web runtime owns subscription.
  • Agent stores tenant or security data in ThreadLocal - use Reactor Context.
  • Agent uses unbounded flatMap - set concurrency according to downstream capacity.
  • Agent wraps JDBC in Mono.just - that still blocks the caller thread.
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 3 skills

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