REAL-TIME WEB-DEVELOPMENT FRONTEND BACKEND JAVASCRIPT HTTP EVENT-DRIVEN API BROWSER PERFORMANCE SCALABILITY USER-EXPERIENCE

Server-Sent Events (SSE): Membangun Fitur Real-time Satu Arah dengan Mudah

⏱️ 13 menit baca
👨‍💻

Server-Sent Events (SSE): Membangun Fitur Real-time Satu Arah dengan Mudah

1. Pendahuluan

Di dunia aplikasi web modern, pengguna semakin mengharapkan pengalaman yang dinamis dan real-time. Bayangkan Anda sedang melihat dashboard analitik yang diperbarui secara langsung, menerima notifikasi instan, atau mengikuti live-feed berita tanpa perlu me-refresh halaman. Fitur-fitur seperti ini mengubah cara kita berinteraksi dengan web.

Untuk mencapai real-time, banyak developer langsung terpikir WebSockets. Memang, WebSockets adalah teknologi yang kuat untuk komunikasi dua arah yang persisten. Namun, bagaimana jika aplikasi Anda hanya membutuhkan satu arah komunikasi? Misalnya, server mengirimkan data ke klien, tetapi klien tidak perlu mengirim data balik secara real-time ke server? Di sini lah Server-Sent Events (SSE) bersinar!

SSE menawarkan solusi yang lebih sederhana dan efisien untuk skenario server-to-client satu arah. Ia dibangun di atas HTTP standar, membuatnya lebih mudah diimplementasikan dan diskalakan dibandingkan WebSockets untuk kasus penggunaan tertentu. Artikel ini akan membawa Anda menyelami SSE, mulai dari konsep dasar hingga implementasi praktis di frontend dan backend, serta kapan sebaiknya memilihnya dibanding WebSockets. Mari kita mulai! 🚀

2. Apa itu Server-Sent Events (SSE)?

📌 Konsep Dasar: Server-Sent Events (SSE) adalah standar web yang memungkinkan server mengirimkan updates secara otomatis ke klien melalui koneksi HTTP yang persisten. Ini adalah bentuk one-way communication dari server ke browser klien.

Bayangkan Anda sedang mendengarkan radio. Stasiun radio (server) terus-menerus memancarkan siaran (event/data), dan Anda (klien) hanya perlu menyetel frekuensi untuk menerimanya. Anda tidak bisa mengirim pesan balik ke stasiun radio melalui siaran itu. Itulah analogi sederhana untuk SSE.

Berbeda dengan mekanisme polling tradisional di mana klien harus berulang kali meminta data baru ke server, SSE memungkinkan server untuk “mendorong” data kapan pun ada informasi baru. Ini mengurangi overhead dan latensi, menghasilkan pengalaman pengguna yang lebih responsif dan efisien.

Kelebihan utama SSE:

3. Bagaimana SSE Bekerja: HTTP Streaming

SSE bekerja dengan memanfaatkan fitur HTTP streaming. Ketika klien membuat koneksi SSE, server tidak menutup koneksi setelah mengirim respons awal. Sebaliknya, server menjaga koneksi tetap terbuka dan terus mengirimkan data baru kapan pun ada.

💡 Header Kunci: Komunikasi SSE diatur oleh header Content-Type: text/event-stream. Header ini memberi tahu browser bahwa respons yang diterima bukanlah halaman web biasa atau file JSON, melainkan aliran event yang terus-menerus.

Setiap “event” atau pesan yang dikirim dari server harus mengikuti format khusus:

data: Pesan pertama
data: Pesan kedua
event: customEventName
data: Pesan untuk custom event
id: 123
data: Pesan dengan ID
retry: 5000

Setiap event diakhiri dengan dua karakter newline (\n\n). Ini memberitahu browser bahwa satu event telah selesai dan siap untuk diproses.

Contoh Alur Kerja:

  1. Klien (browser) membuat koneksi HTTP ke endpoint SSE (misalnya /events).
  2. Server merespons dengan Content-Type: text/event-stream dan menjaga koneksi tetap terbuka.
  3. Server mulai mengirimkan data dalam format SSE ke klien.
  4. Klien menerima data tersebut dan memprosesnya menggunakan API EventSource.
  5. Jika koneksi terputus, browser secara otomatis mencoba menyambung kembali setelah waktu retry yang ditentukan (atau default).

4. Membangun Server SSE (Studi Kasus: Node.js dengan Express)

Untuk membangun server SSE, kita perlu memastikan server kita dapat menjaga koneksi tetap terbuka dan mengirimkan data dalam format text/event-stream. Berikut adalah contoh sederhana menggunakan Node.js dan framework Express.

// server.js
const express = require("express");
const cors = require("cors"); // Untuk mengatasi masalah CORS jika frontend dan backend berbeda domain

const app = express();
const port = 3000;

app.use(cors()); // Aktifkan CORS untuk semua origin (sesuaikan untuk produksi!)
app.use(express.json());

// Array untuk menyimpan objek respons dari klien yang terhubung
const clients = [];

// Endpoint untuk koneksi SSE
app.get("/events", (req, res) => {
  // Set header untuk SSE
  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");
  res.setHeader("Access-Control-Allow-Origin", "*"); // Penting untuk CORS

  // Setiap event akan memiliki ID unik untuk mekanisme retry
  res.setHeader("X-Accel-Buffering", "no"); // Penting untuk Nginx/Apache agar tidak buffer

  // Tambahkan klien ke daftar
  const clientId = Date.now();
  const newClient = {
    id: clientId,
    res,
  };
  clients.push(newClient);

  console.log(`Klien baru terhubung: ${clientId}`);

  // Jika klien memutuskan koneksi, hapus dari daftar
  req.on("close", () => {
    console.log(`Klien ${clientId} terputus`);
    clients.splice(clients.indexOf(newClient), 1);
  });
});

// Endpoint untuk mengirim data ke semua klien yang terhubung
app.post("/send-message", (req, res) => {
  const { message } = req.body;
  if (!message) {
    return res.status(400).send("Pesan tidak boleh kosong.");
  }

  const newEvent = `data: ${message}\n\n`; // Format SSE

  clients.forEach((client) => {
    try {
      client.res.write(newEvent);
    } catch (error) {
      console.error(`Gagal mengirim ke klien ${client.id}:`, error);
      // Klien mungkin sudah terputus, akan dihapus di event 'close'
    }
  });

  res.status(200).send("Pesan berhasil dikirim ke semua klien.");
});

// Contoh endpoint untuk mengirim event kustom
app.post("/send-notification", (req, res) => {
  const { title, body } = req.body;
  if (!title || !body) {
    return res.status(400).send("Judul dan isi notifikasi tidak boleh kosong.");
  }

  const notificationEvent = `event: notification\ndata: ${JSON.stringify({ title, body })}\nid: ${Date.now()}\n\n`;

  clients.forEach((client) => {
    try {
      client.res.write(notificationEvent);
    } catch (error) {
      console.error(`Gagal mengirim notifikasi ke klien ${client.id}:`, error);
    }
  });

  res.status(200).send("Notifikasi berhasil dikirim.");
});

app.listen(port, () => {
  console.log(`Server SSE berjalan di http://localhost:${port}`);
});

Penjelasan Kode Server:

⚠️ Penting untuk Produksi:

5. Menggunakan SSE di Frontend (JavaScript)

Di sisi klien (browser), menggunakan SSE sangatlah mudah berkat API EventSource yang sudah ada di JavaScript.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>SSE Client Example</title>
    <style>
      body {
        font-family: sans-serif;
        margin: 20px;
      }
      #messages,
      #notifications {
        border: 1px solid #ccc;
        padding: 10px;
        min-height: 100px;
        margin-bottom: 20px;
        background-color: #f9f9f9;
      }
      .message-item {
        margin-bottom: 5px;
        padding: 5px;
        background-color: #e0f7fa;
        border-left: 5px solid #00bcd4;
      }
      .notification-item {
        margin-bottom: 5px;
        padding: 8px;
        background-color: #ffe0b2;
        border-left: 5px solid #ff9800;
      }
      h3 {
        margin-top: 30px;
      }
    </style>
  </head>
  <body>
    <h1>Server-Sent Events Demo</h1>

    <h3>General Messages</h3>
    <div id="messages">
      <!-- Pesan dari server akan muncul di sini -->
    </div>

    <h3>Notifications</h3>
    <div id="notifications">
      <!-- Notifikasi dari server akan muncul di sini -->
    </div>

    <script>
      // Inisialisasi EventSource
      const eventSource = new EventSource("http://localhost:3000/events");

      const messagesDiv = document.getElementById("messages");
      const notificationsDiv = document.getElementById("notifications");

      // Handler untuk event 'message' (default event)
      eventSource.onmessage = function (event) {
        console.log("Pesan diterima:", event.data);
        const p = document.createElement("p");
        p.className = "message-item";
        p.textContent = `[${new Date().toLocaleTimeString()}] ${event.data}`;
        messagesDiv.appendChild(p);
        messagesDiv.scrollTop = messagesDiv.scrollHeight; // Scroll ke bawah
      };

      // Handler untuk event kustom 'notification'
      eventSource.addEventListener("notification", function (event) {
        console.log("Notifikasi diterima:", event.data);
        try {
          const notification = JSON.parse(event.data);
          const div = document.createElement("div");
          div.className = "notification-item";
          div.innerHTML = `<strong>${notification.title}</strong><br>${notification.body} <small>[ID: ${event.lastEventId}]</small>`;
          notificationsDiv.appendChild(div);
          notificationsDiv.scrollTop = notificationsDiv.scrollHeight;
        } catch (e) {
          console.error("Gagal parse notifikasi:", e);
        }
      });

      // Handler untuk event 'open' (koneksi berhasil)
      eventSource.onopen = function () {
        console.log("Koneksi SSE berhasil dibuka.");
        const p = document.createElement("p");
        p.className = "message-item";
        p.textContent = `[${new Date().toLocaleTimeString()}] Koneksi SSE berhasil dibuka.`;
        messagesDiv.appendChild(p);
      };

      // Handler untuk event 'error' (koneksi terputus atau gagal)
      eventSource.onerror = function (error) {
        console.error("Terjadi error pada koneksi SSE:", error);
        const p = document.createElement("p");
        p.className = "message-item";
        p.textContent = `[${new Date().toLocaleTimeString()}] Koneksi SSE terputus atau error. Mencoba menyambung kembali...`;
        messagesDiv.appendChild(p);
      };
    </script>
  </body>
</html>

Penjelasan Kode Klien:

6. Kapan Menggunakan SSE vs. WebSockets?

Memilih antara SSE dan WebSockets seringkali menjadi pertanyaan umum. Keduanya memungkinkan komunikasi real-time, tetapi memiliki kasus penggunaan yang berbeda.

🎯 Pilih SSE jika:

Pilih WebSockets jika:

Analogi:

Best Practices dan Pertimbangan

Kesimpulan

Server-Sent Events (SSE) adalah alat yang sangat berguna di arsenal web developer untuk membangun fitur real-time satu arah. Dengan kesederhanaan dan efisiensinya yang dibangun di atas HTTP, SSE seringkali merupakan pilihan yang lebih baik daripada WebSockets untuk skenario seperti notifikasi, live-feed, atau pembaruan status.

Memahami kapan dan bagaimana menggunakan SSE akan membantu Anda merancang aplikasi web yang lebih responsif, efisien, dan memberikan pengalaman pengguna yang lebih baik. Jadi, lain kali Anda membutuhkan komunikasi real-time dari server ke klien, jangan lupakan SSE! Ini mungkin solusi yang Anda cari.

🔗 Baca Juga