In the post learn to build Streaming APIs with Server-Sent Events (SSE) in Spring WebFlux for efficient one-way communication to browsers or clients.

📑 Table of Contents
- What Are Server-Sent Events?
- SSE vs WebSockets vs Polling
- Why Use SSE in Reactive APIs?
- Adding SSE Support in Spring WebFlux
- Implementing an SSE Endpoint
- Consuming SSE in the Browser
- Keeping SSE Connections Alive
- Error Handling & Retry in SSE
- Production Considerations
- Final Thoughts
🔄 1. What Are Server-Sent Events?
Server-Sent Events (SSE) are a unidirectional communication mechanism where the server pushes updates to the client over HTTP. They use the text/event-stream content type and are part of the HTML5 standard.
Unlike WebSockets, SSE is simpler, requires no special protocol, and works over standard HTTP/1.1.
⚔️ 2. SSE vs WebSockets vs Polling
| Feature | SSE | WebSockets | Polling |
|---|---|---|---|
| Direction | Server → Client (One-way) | Bi-directional | Client → Server (repeated) |
| Protocol | HTTP | Custom TCP (ws://) | HTTP |
| Simplicity | Very simple | Complex | Very simple |
| Browser Support | Excellent (all modern) | Good (requires JS libs) | Excellent |
| Use Case | Stock Tickers, Notifications | Chat, Games | Fallback, low updates |
⚡ 3. Why Use SSE in Reactive APIs?
Spring WebFlux shines when combined with SSE:
- Native support for
text/event-stream - Easily returns
Flux<T>directly from controller - Uses Reactor’s backpressure model
- Perfect for notifications, sensor data, logs, etc.
🧱 4. Adding SSE Support in Spring WebFlux
Ensure you have this dependency:
org.springframework.boot
spring-boot-starter-webflux
Spring WebFlux automatically supports SSE when you return a Flux<T> and set the response type to MediaType.TEXT_EVENT_STREAM.
👨💻 5. Implementing an SSE Endpoint
Here’s a simple example that emits a stream of numbers every second:
package com.kscodes.springboot.reactive.controller;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.time.Duration;
@RestController
public class SseController {
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux streamData() {
return Flux.interval(Duration.ofSeconds(1))
.map(seq -> "Event " + seq);
}
}
🧠 How It Works:
- The controller returns a
Flux<String> - Spring serializes it as
text/event-stream - The browser keeps the connection open and renders each line as an event
🌐 6. Consuming SSE in the Browser
JavaScript Example:
< script>
const eventSource = new EventSource("http://localhost:8080/stream");
eventSource.onmessage = function (event) {
console.log("New event:", event.data);
};
< /script>
✅ Works in all modern browsers including Chrome, Firefox, Safari, and Edge.
♻️ 7. Keeping SSE Connections Alive
Most proxies and load balancers kill idle connections. To prevent that:
- Send heartbeat events every few seconds:
Flux.interval(Duration.ofSeconds(10))
.map(i -> "heartbeat - " + i);
Use Cache-Control: no-transform and Connection: keep-alive headers if needed
🛠️ 8. Error Handling & Retry in SSE
SSE supports built-in retry behavior.
You can add retry time and id:
@GetMapping(value = "/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> events() {
return Flux.interval(Duration.ofSeconds(1))
.map(seq -> ServerSentEvent.<String>builder()
.id(String.valueOf(seq))
.event("message")
.data("Update " + seq)
.retry(Duration.ofSeconds(5))
.build());
}
This enables the browser to resume automatically on disconnects.
🚀 9. Production Considerations
- Timeouts: Configure reverse proxies like NGINX to allow long-lived connections
- Load Balancing: Sticky sessions or consistent hashing might be required
- Security: Add headers like
X-Accel-Buffering: noto prevent buffering by proxies - Monitoring: SSE endpoints are stateful—monitor active connections
✅ 10. Final Thoughts
Server-Sent Events offer a lightweight way to stream real-time data to the browser using Spring WebFlux and Flux. It’s ideal for use cases that don’t require two-way communication but need frequent updates — like logs, live notifications, system metrics, or IoT data.