Building Reactive Applications with WebFlux

1. Adding Spring WebFlux Dependencies

Example: WebFlux dependency

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
Warning: Don't include spring-boot-starter-web together — Boot picks the wrong server type.

2. Understanding Reactive Streams

Component Role
Publisher<T> Emits items asynchronously
Subscriber<T> Consumes items + signals
Subscription Backpressure control: request(n), cancel()
Processor Both publisher + subscriber

3. Using Mono for Single Values

Example: Mono: map and default value

Mono<User> user = userRepo.findById(id);
Mono<String> name = user.map(User::getName).defaultIfEmpty("anonymous");
Mono.empty(); Mono.just(x); Mono.error(ex);

4. Using Flux for Multiple Values

Example: Flux: filter and limit stream

Flux<Order> recent = orderRepo.findByCreatedAfter(Instant.now().minus(1, HOURS))
  .filter(o -> o.total().compareTo(BigDecimal.TEN) > 0)
  .take(100);

5. Creating Reactive REST Controllers

Example: Reactive CRUD controller

@RestController
@RequestMapping("/api/users")
public class UserApi {
  private final UserRepo repo;
  public UserApi(UserRepo repo) { this.repo = repo; }
  @GetMapping public Flux<User> all() { return repo.findAll(); }
  @GetMapping("/{id}") public Mono<ResponseEntity<User>> one(@PathVariable Long id) {
    return repo.findById(id).map(ResponseEntity::ok).defaultIfEmpty(ResponseEntity.notFound().build());
  }
  @PostMapping public Mono<User> create(@RequestBody Mono<User> body) {
    return body.flatMap(repo::save);
  }
}

6. Implementing Reactive Repositories

Interface For
ReactiveCrudRepository Generic reactive CRUD
ReactiveSortingRepository + Sort
R2dbcRepository R2DBC SQL DBs
ReactiveMongoRepository MongoDB
ReactiveRedisRepository Redis

7. Using Reactive Database Drivers (R2DBC)

Example: R2DBC datasource configuration

spring:
  r2dbc:
    url: r2dbc:postgresql://localhost/app
    username: app
    password: ${DB_PASS}

8. Handling Backpressure Strategies

Operator Strategy
onBackpressureBuffer() Buffer overflow
onBackpressureDrop() Drop excess items
onBackpressureLatest() Keep latest only
onBackpressureError() Signal error
limitRate(n) Cap upstream demand

9. Implementing Server-Sent Events

Example: Server-sent events stream with Flux

@GetMapping(value="/events", produces=MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<Quote>> quotes() {
  return Flux.interval(Duration.ofSeconds(1))
    .map(i -> ServerSentEvent.<Quote>builder()
      .id(String.valueOf(i)).event("quote").data(quoteService.next()).build());
}

10. Testing Reactive Endpoints (WebTestClient)

Example: Test reactive endpoint with WebTestClient

@WebFluxTest(UserApi.class)
class UserApiTests {
  @Autowired WebTestClient client;
  @MockBean UserRepo repo;
  @Test void list() {
    when(repo.findAll()).thenReturn(Flux.just(new User(1L,"a")));
    client.get().uri("/api/users").exchange()
      .expectStatus().isOk().expectBodyList(User.class).hasSize(1);
  }
}

11. Using Reactive Operators

Operator Purpose
map / flatMap Transform / chain async
filter Predicate
zip / merge / concat Combine
switchIfEmpty / defaultIfEmpty Empty fallback
retry / retryWhen Re-subscribe on error
timeout(d) Error if no signal
publishOn / subscribeOn Switch scheduler
cache() Replay to late subscribers