javaadvanced

Spring WebFlux — Reactive REST API

Build reactive REST APIs with Spring WebFlux using Mono, Flux, and non-blocking operations.

java
import org.springframework.web.bind.annotation.*;
import org.springframework.http.HttpStatus;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;

@RestController
@RequestMapping("/api/reactive/users")
public class ReactiveUserController {
    private final ReactiveUserRepository repo;

    public ReactiveUserController(ReactiveUserRepository repo) {
        this.repo = repo;
    }

    // Return stream of users
    @GetMapping(produces = "application/json")
    public Flux<UserDTO> findAll() {
        return repo.findAll()
            .map(this::toDTO)
            .timeout(Duration.ofSeconds(5));
    }

    // Return single user
    @GetMapping("/{id}")
    public Mono<UserDTO> findById(@PathVariable String id) {
        return repo.findById(id)
            .map(this::toDTO)
            .switchIfEmpty(Mono.error(new NotFoundException("User not found")));
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Mono<UserDTO> create(@RequestBody Mono<CreateUserRequest> request) {
        return request
            .map(req -> new User(null, req.name(), req.email()))
            .flatMap(repo::save)
            .map(this::toDTO);
    }

    // Parallel calls — combine results
    @GetMapping("/{id}/dashboard")
    public Mono<DashboardDTO> dashboard(@PathVariable String id) {
        Mono<User> user = repo.findById(id);
        Mono<List<Order>> orders = orderRepo.findByUserId(id).collectList();
        Mono<Stats> stats = statsService.getForUser(id);

        return Mono.zip(user, orders, stats)
            .map(tuple -> new DashboardDTO(
                toDTO(tuple.getT1()),
                tuple.getT2(),
                tuple.getT3()
            ));
    }

    // SSE streaming
    @GetMapping(value = "/stream", produces = "text/event-stream")
    public Flux<UserDTO> stream() {
        return repo.findAll()
            .map(this::toDTO)
            .delayElements(Duration.ofMillis(500));
    }

    private UserDTO toDTO(User u) {
        return new UserDTO(u.id(), u.name(), u.email());
    }
}

Sponsored

Railway

Use Cases

  • High-throughput non-blocking APIs
  • Server-Sent Events for real-time streaming
  • Combining parallel async data sources

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.