spring-reactive

star 1

Build reactive APIs and services with Spring WebFlux, Project Reactor (Mono, Flux), WebClient, and reactive operators. Capabilities include MDC context propagation (ContextRegistry, Hooks.enableAutomaticContextPropagation), WebClient configuration (timeouts, error handling, retry), reactive operator chains (flatMap, switchIfEmpty, zip), and blocking detection. Use when implementing WebFlux endpoints, propagating MDC/trace context across reactive boundaries, configuring WebClient, debugging context loss, handling backpressure, or testing reactive streams with StepVerifier.

jander99 By jander99 schedule Updated 2/11/2026

name: spring-reactive description: Build reactive APIs and services with Spring WebFlux, Project Reactor (Mono, Flux), WebClient, and reactive operators. Capabilities include MDC context propagation (ContextRegistry, Hooks.enableAutomaticContextPropagation), WebClient configuration (timeouts, error handling, retry), reactive operator chains (flatMap, switchIfEmpty, zip), and blocking detection. Use when implementing WebFlux endpoints, propagating MDC/trace context across reactive boundaries, configuring WebClient, debugging context loss, handling backpressure, or testing reactive streams with StepVerifier. license: MIT metadata: version: 1.0.0 audience: developers workflow: backend-development

Spring Reactive

Build non-blocking, reactive web applications with Spring WebFlux and Project Reactor. This skill focuses on production patterns, especially MDC context propagation which is the #1 pain point in reactive applications.

When to Use Me

  • Implement reactive REST endpoints with WebFlux
  • Propagate MDC context (traceId, requestId) across async boundaries
  • Configure WebClient with proper timeouts, retries, and error handling
  • Debug "context loss" issues where traceId disappears in logs
  • Chain reactive operators (Mono/Flux) correctly
  • Detect and handle blocking calls in reactive code
  • Test reactive streams with StepVerifier

What I Do

  • Guide MDC context propagation setup with ContextRegistry and Hooks
  • Provide WebClient configuration patterns (connection pools, timeouts)
  • Explain Mono/Flux operator selection and chaining
  • Help debug context loss and blocking call issues
  • Show StepVerifier testing patterns
  • Cover error handling strategies in reactive streams

MDC Context Propagation

This is critical. Without proper setup, MDC context (traceId, spanId, requestId) is lost when crossing reactive boundaries.

Required Configuration (Spring Boot 3.x / Reactor 3.5+)

@Configuration
public class ReactorContextConfig {
    @PostConstruct
    public void setupContextPropagation() {
        // Enable automatic context propagation (Reactor 3.5+)
        Hooks.enableAutomaticContextPropagation();

        // Register MDC accessor with null-safe handling
        ContextRegistry.getInstance().registerThreadLocalAccessor(
            "mdc",
            () -> Optional.ofNullable(MDC.getCopyOfContextMap()).orElse(Map.of()),
            map -> { if (map != null) MDC.setContextMap(map); },
            MDC::clear
        );
    }
}

Key Points

  • Use Micrometer context-propagation integration for production (recommended over DIY)
  • Context flows per-subscription within the same reactive chain
  • Use deferContextual to read Reactor Context; don't assume operators "drop" context
  • WebFilter captures context: .contextWrite(ctx -> ctx.put("mdc", contextMap))

Reactor Patterns

Mono/Flux Selection

Use Case Type Why
Single value or empty Mono<T> 0..1 elements
Collection/stream Flux<T> 0..N elements
Void operation Mono<Void> Side effects only
Optional result Mono<T> with switchIfEmpty Handle empty case

Common Operators

// Transform
mono.map(x -> transform(x))           // Sync
mono.flatMap(x -> asyncCall(x))       // Async

// Handle empty
mono.switchIfEmpty(Mono.defer(() -> fallback()))
mono.defaultIfEmpty(defaultValue)

// Error handling
mono.onErrorResume(ex -> fallbackMono())
mono.onErrorMap(ex -> new CustomException(ex))

// Combine
Mono.zip(mono1, mono2, (a, b) -> combine(a, b))
Flux.merge(flux1, flux2)  // Interleaved
Flux.concat(flux1, flux2) // Sequential

Anti-Patterns

// BAD: Blocking in reactive chain
mono.map(x -> blockingCall(x))  // Blocks event loop!

// GOOD: Offload to bounded scheduler
mono.flatMap(x -> Mono.fromCallable(() -> blockingCall(x))
    .subscribeOn(Schedulers.boundedElastic()))

WebClient Configuration

Configure HttpClient with timeouts, add MDC propagation filter via Mono.deferContextual(), handle errors with .onStatus(), retry with Retry.backoff().

Blocking Detection

Violation Solution
Thread.sleep() Mono.delay(Duration)
JDBC calls Use R2DBC or subscribeOn(Schedulers.boundedElastic())
InputStream.read() Use DataBufferUtils
Synchronized blocks Use Mono.fromCallable().subscribeOn()

Enable BlockHound in dev/test to detect blocking violations automatically.

Common Errors

Error Cause Solution
MDC values null in logs Context not propagated Enable Hooks.enableAutomaticContextPropagation()
block()/blockFirst() error Blocking on event loop Use subscribeOn(Schedulers.boundedElastic())
Timeout on blocking read WebClient timeout Configure timeouts in HttpClient
Context is empty New Mono without context Use Mono.deferContextual() or contextWrite()

Testing with StepVerifier

@Test
void shouldProcessReactively() {
    StepVerifier.create(service.process("input"))
        .expectNext("expected")
        .verifyComplete();
}

@Test
void shouldPropagateContext() {
    Mono<String> result = service.getTraceId()
        .contextWrite(ctx -> ctx.put("mdc", Map.of("traceId", "test-123")));

    StepVerifier.create(result)
        .expectNext("test-123")
        .verifyComplete();
}

WebTestClient Testing

@WebFluxTest(OrderController.class)
class OrderControllerTest {
    @Autowired
    private WebTestClient webClient;

    @MockBean
    private OrderService orderService;

    @Test
    void shouldReturnOrder() {
        when(orderService.findById(1L)).thenReturn(Mono.just(order));

        webClient.get().uri("/orders/1")
            .exchange()
            .expectStatus().isOk()
            .expectBody(Order.class).isEqualTo(order);
    }
}

Backpressure Basics

Strategy Use When
onBackpressureBuffer() Slow consumer, bounded buffer OK
onBackpressureDrop() Latest value matters, can drop old
limitRate(n) Control request batch size

Context7 Integration

For up-to-date Spring WebFlux documentation:

libraryName: "Spring WebFlux" or "Project Reactor"
Queries: "WebClient configuration", "Mono Flux operators", "Context propagation"

Reference Navigation

Topic Reference Load When
Full MDC WebFilter research.md Complete implementation
WebClient advanced config research.md Connection pool tuning, DNS resolver
Operator deep dive research.md Complete operator catalog
BlockHound setup research.md Blocking detection configuration

Related Skills

Skill Use For
spring-boot-core General Spring Boot patterns
spring-testing Testing strategies including WebTestClient
opentelemetry-tracing Distributed tracing setup

Resources

Install via CLI
npx skills add https://github.com/jander99/skills --skill spring-reactive
Repository Details
star Stars 1
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator