Episode ini membahas Server-Sent Events di Fiber v3: mengalirkan event realtime dari server ke browser dengan Content-Type text/event-stream, sendStream untuk chunk, client EventSource, keep-alive, serta hook events saat koneksi dibuka dan ditutup.

Sebagian besar komunikasi HTTP bersifat request-response sekali jalan. Episode 13 membahas pengecualiannya: Server-Sent Events (SSE) — cara server mengalirkan event realtime ke browser melalui satu koneksi HTTP yang tetap terbuka.
SSE adalah pilihan tepat untuk notifikasi, feed berita, atau dashboard live. Berbeda dengan WebSocket, SSE hanya satu arah (server ke client), tapi jauh lebih sederhana: cukup HTTP biasa, dengan Content-Type: text/event-stream dan library standar EventSource di browser.
Protokol SSE sederhana: setiap event adalah teks berformat field: value. Bidang yang dikenal: data, event, id, dan retry:
data: {"message": "hello"}
event: user-joined
data: {"name": "Budi"}
Satu baris data: membawa payload; baris kosong menandakan akhir sebuah event. Bidang event: memberi nama event agar client bisa mendengarkannya secara spesifik. Dengan id: client bisa mengirim ulang event yang terlewat, dan retry: mengatur jeda koneksi ulang.
Di Fiber, SSE diimplementasikan lewat c.SendStream dengan header yang tepat:
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 dipanggil berulang untuk menghasilkan chunk; masing-masing chunk diformat sebagai event data: {...}. Header text/event-stream dan keep-alive memberi tahu browser agar koneksi tidak ditutup dan respons tidak di-cache.
Koneksi SSE bisa hidup lama. Penting untuk mendeteksi saat client menutup koneksi dan menjalankan kode pembersihan:
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() mengembalikan true saat client sudah pergi. Dengan mengeceknya setiap iterasi, server bisa menghentikan pekerjaan latar belakang dan mengembalikan io.EOF untuk mengakhiri stream — mencegah goroutine bocor.
Di sisi browser, client memakai EventSource — API standar, tanpa library tambahan:
<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") membuka koneksi SSE ke endpoint Fiber. Event tanpa nama memicu listener message; event bernama seperti user-joined memicu es.addEventListener("user-joined", ...). EventSource menangani reconnect otomatis sesuai nilai retry.
curl -N http://localhost:3000/eventscurl -N menonaktifkan buffering sehingga event langsung tercetak ke terminal. Kalian akan melihat baris data: {...} terus muncul setiap detik — bukti stream berjalan. Buka juga /feed dari browser dan amati console: client menerima data: ping berulang.
Inti yang harus dibawa pulang:
data: diakhiri baris kosong; bisa diberi event, id, dan retry.Content-Type: text/event-stream, Connection: keep-alive, Cache-Control: no-cache.c.SendStream dengan StreamParams{Chunked: true} menghasilkan chunk event.c.Context().IsClosed() mendeteksi koneksi tertutup agar goroutine tidak bocor.EventSource standar dengan reconnect otomatis.Di episode 14 selanjutnya kita membahas WebSocket — upgrade koneksi dengan package fiber/websocket, handler berbasis context, dan integrasi dengan client browser.