Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Spring WebFlux SSE: emit bounded receipt events and test HTTP encoding

Last updated: 29 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

A WebFlux controller can return a Flux of ServerSentEvent values that the HTTP message writer encodes as an event stream.

Download Spring source kit

The downloadable Spring source kit pins Java 21 and Spring Boot 4.0.8 with its managed dependencies. Run mvn test to check the named fixture.

Define the event contract

ReactiveReceiptController accepts a count from one through five and emits R1 onward with an id and a receipt event name. Invalid counts throw a bad-request response before the Flux is returned. The mock HTTP test checks status, event-stream content type, ordered ids and data, and normal completion after three events.

The range emits a finite sequence with a ten-millisecond delay between items, not a durable event source. The delay has no production latency meaning. A raw Flux.interval can fail when downstream requests arrive slower than its ticks; this fixture uses a demand-aware finite range instead. This endpoint supplies neither persisted positions nor replay after a reconnect; a browser Last-Event-ID header cannot recover missed business events unless the server implements a replay contract.

Keep blocking work out of the sequence

The fixture does not query JPA in its emission callback. Putting a blocking repository call inside map would still block the executing thread; changing a return type does not convert that database driver into nonblocking I/O. Demand foundations and JPA repositories address separate execution models.

WebTestClient binds directly to the controller, exercising request mapping and codecs without a listening socket. It does not test server adapter queues, reverse-proxy buffering, disconnect behavior across a real network or deployment capacity. Test those paths before making delivery or memory claims.

Checked source

Java
package in.aitrove.contracts;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicBoolean;
import org.springframework.http.*;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import reactor.core.publisher.Flux;
@RestController
public class ReactiveReceiptController {
    public final AtomicBoolean cancelled=new AtomicBoolean();
    @GetMapping(value="/contract/events",produces=MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> events(@RequestParam(defaultValue="3") int count) {
        if(count<1 || count>5)throw new ResponseStatusException(HttpStatus.BAD_REQUEST,"count must be 1..5");
        return Flux.range(1,count).delayElements(Duration.ofMillis(10))
            .map(index->ServerSentEvent.<String>builder("R"+index).id(Integer.toString(index)).event("receipt").build())
            .doOnCancel(()->cancelled.set(true));
    }
}

Test the boundary

ReactiveHttpContractTest.emitsOrderedSseEventsAndCompletes and rejectsCountBeforeSuccessfulStreamHeaders checks this contract in the source kit. Excerpts belong to the named classes; use the downloadable files for imports, configuration and assertions.

Costs and boundaries

The fixture emits at most five events and retains no durable history. A deployed stream holds a subscription and connection for each client; proxy buffers, timeouts and upstream operator queues can add retained state. The mock HTTP test makes no throughput or end-to-end memory guarantee.

Common Mistakes

  • Validate the count before returning a successful streaming response.
  • Do not put blocking repository work on an event-loop path.
  • Do not claim replay or durable delivery from a delayed range publisher.

Read next

Spring reactive foundations: request values and cancel a subscription, Spring WebFlux cancellation: observe downstream cleanup without undoing work, Spring MVC request validation: reject invalid commands before mutation, Java Flow: request items and observe publisher completion.

spring
spring-boot
webflux-sse
Storage details