Spring Real-Time Events
This guide adds an SSE events proxy endpoint to the Spring tutorial app. For full details on event kinds, format, and connection lifecycle, see Real-Time Events.
Prerequisites
Starting checkpoint: java/spring/examples/doc-checkpoints/03-with-history
SSE Events Proxy
Checkpoint 03b adds an events proxy endpoint that forwards SSE from Memory Service to the frontend:
package com.example.demo;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.chirino.memoryservice.client.MemoryServiceProxy;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
@RestController
class EventsController {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private final MemoryServiceProxy proxy;
EventsController(MemoryServiceProxy proxy) {
this.proxy = proxy;
}
@GetMapping(value = "/v1/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
SseEmitter streamEvents(@RequestParam(required = false) String kinds) {
SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
proxy.streamEvents(kinds)
.filter(EventsController::isFrontendVisible)
.subscribe(
event -> {
try {
emitter.send(
OBJECT_MAPPER.writeValueAsString(event),
MediaType.APPLICATION_JSON);
} catch (Exception e) {
emitter.completeWithError(e);
}
},
emitter::completeWithError,
emitter::complete);
return emitter;
}
private static boolean isFrontendVisible(MemoryServiceProxy.EventNotification event) {
if (!"entry".equals(event.kind())) {
return true;
}
Object channel = event.data() == null ? null : event.data().get("entry_channel");
return "history".equals(channel);
}
} Why proxy? The agent app sits between the frontend and Memory Service. Proxying the SSE stream lets the app forward the caller’s Bearer token for authorization while injecting the agent’s API key for service authentication. Frontends never talk directly to Memory Service.
The checkpoint handler forwards only history-channel entry notifications to browser clients. Context-channel entries are agent-internal and can include prompt context, summaries, retrieval results, or tool payloads.
Connecting
Subscribe to events through the agent app:
curl -N -H "Authorization: Bearer $(get-token)" \
http://localhost:9090/v1/events
Filter to specific event kinds:
curl -N -H "Authorization: Bearer $(get-token)" \
"http://localhost:9090/v1/events?kinds=conversation,entry"
Trusted backend processors can subscribe directly to Memory Service with explicit entry filters such as entry_channels=history,context. Browser-facing Spring routes should keep their history-only entry forwarding rule.
Next Steps
- Real-Time Events — full reference for event kinds, format, and connection lifecycle
- Service Configuration — event bus settings