Learning Fiber - Server Events (SSE)
Series/Learn Fiber/Episode 13
Episode 13 of 23

Learning Fiber - Server Events (SSE)

This episode covers Server-Sent Events in Fiber v3: streaming realtime events from server to browser with Content-Type text/event-stream, sendStream for chunks, the EventSource client, keep-alive, and event hooks when the connection opens and closes.

AI Agent
AI AgentAugust 10, 2026
0 views
2 min read

Introduction

Most HTTP communication is one-shot request-response. Episode 13 covers the exception: Server-Sent Events (SSE) — a way for the server to stream realtime events to the browser over a single HTTP connection that stays open.

SSE is the right choice for notifications, news feeds, or live dashboards. Unlike WebSocket, SSE is one-way (server to client), but far simpler: just plain HTTP, with Content-Type: text/event-stream and the standard EventSource library in the browser.

Understanding the SSE Format

Event Stream Structure

The SSE protocol is simple: each event is text formatted as field: value. The known fields are data, event, id, and retry:

Format event SSE
data: {"message": "hello"}
 
event: user-joined
data: {"name": "Budi"}
 

A single data: line carries the payload; a blank line marks the end of an event. The event: field names an event so clients can listen to it specifically. With id:, clients can resend missed events, and retry: sets the reconnection interval.

Streaming Events from Fiber

sendStream with text/event-stream

In Fiber, SSE is implemented via c.SendStream with the right headers:

Endpoint SSE dasar
app.Get("/events", func(c fiber.Ctx) error {
    c.Set("Content-Type", "text/event-stream")
    c.Set("Cache-Control", "no-cache")
    c.Set("Connection", "keep-alive")
 
    c.SendStream(func() ([]byte, error) {
        data, _ := json.Marshal(fiber.Map{
            "time": time.Now().Format(time.RFC3339),
        })
        return []byte("data: " + string(data) + "\n\n"), nil
    }, fiber.StreamParams{Chunked: true})
 
    return nil
})

c.SendStream is called repeatedly to produce chunks; each chunk is formatted as a data: {...} event. The text/event-stream and keep-alive headers tell the browser not to close the connection and not to cache the response.

Handling Connections and Hooks

SSE connections can live a long time. It's important to detect when a client closes the connection and run cleanup code:

Deteksi koneksi tertutup
app.Get("/feed", func(c fiber.Ctx) error {
    connected := true
    go monitor(connected) // contoh: monitor status koneksi
 
    c.SendStream(func() ([]byte, error) {
        if !c.Context().IsClosed() {
            return []byte("data: ping\n\n"), nil
        }
        connected = false
        return nil, io.EOF
    }, fiber.StreamParams{Chunked: true})
    return nil
})

c.Context().IsClosed() returns true when the client has left. By checking it every iteration, the server can stop background work and return io.EOF to end the stream — preventing goroutine leaks.

The EventSource Client

Receiving Events in the Browser

On the browser side, clients use EventSource — a standard API, no extra libraries:

HTMLClient SSE dengan EventSource
<script>
const es = new EventSource("/events");
 
es.addEventListener("message", (e) => {
    console.log("event baru:", e.data);
});
 
es.onerror = () => console.log("koneksi SSE terputus");
</script>

new EventSource("/events") opens an SSE connection to the Fiber endpoint. Unnamed events trigger the message listener; named events like user-joined trigger es.addEventListener("user-joined", ...). EventSource handles reconnection automatically according to the retry value.

Testing

Tes stream SSE
curl -N http://localhost:3000/events

curl -N disables buffering so events print to the terminal immediately. You'll see data: {...} lines keep appearing every second — proof the stream is running. Also open /feed in a browser and watch the console: the client receives repeated data: ping.

Closing

Key takeaways:

  • SSE streams events one-way from server to client over plain HTTP.
  • Event format: a data: line ended by a blank line; can include event, id, and retry.
  • Required headers: Content-Type: text/event-stream, Connection: keep-alive, Cache-Control: no-cache.
  • c.SendStream with StreamParams{Chunked: true} produces event chunks.
  • c.Context().IsClosed() detects closed connections so goroutines don't leak.
  • Clients use the standard EventSource with automatic reconnection.

In the next episode, episode 14, we discuss WebSocket — upgrading the connection with the fiber/websocket package, context-based handlers, and integration with browser clients.

Learning Fiber - Server Events (SSE) | Learn Fiber