Tutorial SSE (Server-Sent Events): Solusi Real-time Efisien

Ringkasan
- Apa itu SSE: Mengenal Server-Sent Events sebagai alternatif WebSocket yang lebih ringan untuk komunikasi satu arah (Server ke Client).
- Kelebihan Utama: Mengapa SSE lebih unggul untuk sistem antrian, notifikasi, dan live-feed berkat fitur auto-reconnect dan protokol HTTP standar.
- Implementasi Next.js: Panduan langkah demi langkah membangun sistem panggilan antrian real-time menggunakan App Router dan EventEmitter.
- Strategi Skalabilitas: Cara meningkatkan performa sistem dari local emitter ke Redis Pub/Sub untuk lingkungan produksi multi-instance.
Saat membangun aplikasi real-time, banyak developer yang secara refleks langsung memilih WebSocket. Memang, WebSocket sangat powerful, tetapi seringkali menjadi overkill untuk kebutuhan yang sederhana. Bayangkan Anda sedang membangun sistem Display TV Panggilan Antrian; apakah TV tersebut perlu mengirim data balik ke server secara terus-menerus? Tentu tidak. TV hanya perlu "mendengarkan" kapan server memanggil nomor antrian berikutnya.
Di sinilah Server-Sent Events (SSE) hadir sebagai pahlawan yang tidak terduga. SSE adalah standar komunikasi yang memungkinkan server mendorong (push) data ke browser secara real-time melalui koneksi HTTP yang tetap terbuka. Bagi Anda yang mencari solusi yang lebih simpel, efisien, dan ramah infrastruktur, SSE adalah jawabannya.
Apa Itu Server-Sent Events (SSE)?
Server-Sent Events adalah teknologi yang memungkinkan server mengirimkan data ke web page secara otomatis tanpa perlu client meminta data tersebut berulang kali (polling). Berbeda dengan HTTP request biasa yang bersifat request-response, SSE menciptakan aliran data (stream) yang tetap terbuka.
SSE bekerja di atas protokol HTTP standar. Browser menggunakan API EventSource untuk membuka koneksi ke server, dan server merespons dengan header khusus text/event-stream. Selama koneksi ini terbuka, server dapat mengirimkan pesan kapan saja dalam format teks tertentu.
Perbandingan Mendalam: SSE vs WebSocket
Untuk membantu Anda menentukan pilihan, mari kita bedah perbedaan keduanya melalui tabel berikut:
| Fitur | SSE (Server-Sent Events) | WebSocket |
|---|---|---|
| Arah Komunikasi | Satu arah (Server → Client) | Dua arah (Bi-directional / Full-duplex) |
| Protokol | Standar HTTP / HTTP/2 | TCP kustom (Diawali HTTP Upgrade) |
| Fitur Reconnect | Otomatis bawaan browser | Manual (Harus koding retry mechanism) |
| Dukungan Proxy/Firewall | Sangat ramah (Tanpa konfigurasi khusus) | Perlu konfigurasi khusus di Edge/Proxy |
| Format Data | Teks (UTF-8) | Teks dan Biner (Binary) |
| Penggunaan Terbaik | Live status board, notifikasi, feed update, AI streaming | Chat app, game multiplayer, collaborative tools |
Mengapa SSE Lebih Cocok untuk Sistem Antrian Online?
Dalam skenario Display TV Panggilan Antrian, kebutuhan utamanya adalah menampilkan nomor yang dipanggil secara instan saat petugas menekan tombol panggil di konsol operator. Berikut adalah alasan mengapa SSE jauh lebih unggul untuk kasus ini:
1. Komunikasi Satu Arah yang Efisien
Display TV hanya bertindak sebagai "pendengar" (read-only broadcast). TV tidak perlu mengirim data apa pun kembali ke server melalui koneksi stream. Menggunakan WebSocket untuk kebutuhan ini ibarat menggunakan jalan tol dua arah untuk kendaraan yang hanya berjalan satu arah; tidak efisien dan memboroskan sumber daya server.
2. Arsitektur yang Lebih Sederhana
Dengan SSE, Anda tidak perlu mengubah seluruh paradigma komunikasi aplikasi Anda. Proses perubahan data (mutation), seperti memanggil nomor antrian, tetap menggunakan route HTTP standar (misalnya POST /api/queue/call). Server kemudian cukup menyiarkan (broadcast) peristiwa tersebut via SSE kepada semua client yang terhubung.
3. Auto-Reconnect Bawaan
Koneksi jaringan di area publik (seperti ruang tunggu) seringkali tidak stabil. Jika koneksi TV terputus sejenak, objek EventSource bawaan browser akan secara otomatis mencoba menyambung ulang tanpa perlu Anda menulis logika retry yang kompleks di sisi frontend.
4. Ramah Infrastruktur dan Firewall
Karena berjalan di atas HTTP biasa, SSE tidak akan terblokir oleh firewall perusahaan atau load balancer yang ketat. WebSocket seringkali membutuhkan konfigurasi khusus pada proxy (seperti Nginx atau AWS ALB) untuk menangani protocol upgrade, sementara SSE bekerja "out-of-the-box".
Panduan Implementasi: Next.js App Router (Route Handler)
Mari kita praktikkan. Kita akan membangun sistem sederhana di mana operator memicu panggilan, dan Display TV menerima update tersebut secara real-time.
Langkah 1: Setup Event Emitter (lib/queue-events.ts)
Karena kita berada di lingkungan Node.js, kita membutuhkan cara untuk mengirim sinyal dari satu route (Operator) ke route lain (SSE Stream). Kita akan menggunakan EventEmitter.
import { EventEmitter } from 'events';
// Menghindari duplikasi instance saat development hot-reload di Next.js
const globalForEvents = global as unknown as { queueEmitter: EventEmitter };
export const queueEmitter =
globalForEvents.queueEmitter || new EventEmitter();
if (process.env.NODE_ENV !== 'production') {
globalForEvents.queueEmitter = queueEmitter;
}
Langkah 2: Buat Endpoint SSE Stream (app/api/queue/events/route.ts)
Endpoint ini akan menjaga koneksi tetap terbuka dan mengirimkan data setiap kali ada event 'call'.
import { queueEmitter } from '@/lib/queue-events';
export const dynamic = 'force-dynamic';
export async function GET() {
const encoder = new TextEncoder();
const stream = new ReadableStream({
start(controller) {
// Handler saat ada pemanggilan antrian baru
const onQueueChange = (data: any) => {
// Format SSE harus diawali dengan 'event:' dan diakhiri dengan dua newline '\n\n'
const payload = `event: queue\ndata: ${JSON.stringify(data)}\n\n`;
controller.enqueue(encoder.encode(payload));
};
// Dengarkan event 'call' dari event emitter
queueEmitter.on('call', onQueueChange);
// Tangani saat client/browser terputus untuk mencegah memory leak
return () => {
queueEmitter.off('call', onQueueChange);
};
},
});
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache, no-transform',
'Connection': 'keep-alive',
},
});
}
Langkah 3: Trigger Event dari Operator (app/api/queue/call/route.ts)
Saat petugas memanggil nomor, kita mengirim request POST yang kemudian akan memicu queueEmitter.
import { NextResponse } from 'next/server';
import { queueEmitter } from '@/lib/queue-events';
export async function POST(request: Request) {
const body = await request.json();
const { counterId, displayNumber } = body;
// 1. Simpan/Update status di Database Anda di sini...
// await db.queue.update({ ... })
// 2. Broadcast perubahan data ke semua subscriber SSE
queueEmitter.emit('call', {
type: 'call',
counterId,
displayNumber,
timestamp: Date.now(),
});
return NextResponse.json({ success: true });
}
Langkah 4: Konsumsi SSE di Display TV (React Client Component)
Di sisi frontend, kita menggunakan EventSource untuk mendengarkan stream data.
'use client';
import { useEffect, useState } from 'react';
export default function DisplayTV() {
const [currentCall, setCurrentCall] = useState<any>(null);
useEffect(() => {
// Buka koneksi SSE ke endpoint server
const eventSource = new EventSource('/api/queue/events');
// Mendengarkan event spesifik dengan nama 'queue'
eventSource.addEventListener('queue', (event) => {
const data = JSON.parse(event.data);
if (data.type === 'call') {
setCurrentCall(data);
// Tips: Tambahkan fungsi Text-to-Speech (Web Speech API) di sini
// window.speechSynthesis.speak(new SpeechSynthesisUtterance(`Nomor antrian ${data.displayNumber}, silahkan menuju loket ${data.counterId}`));
}
});
eventSource.onerror = (err) => {
console.error('SSE Error, reconnecting...', err);
};
return () => {
eventSource.close();
};
}, []);
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-slate-900 text-white">
<h1 className="text-4xl font-bold mb-8">PANGGILAN ANTRIAN</h1>
{currentCall ? (
<div className="text-center bg-slate-800 p-12 rounded-2xl shadow-2xl border border-slate-700">
<p className="text-2xl text-slate-400 mb-2">NOMOR ANTRIAN</p>
<p className="text-9xl font-black text-amber-400 tracking-wider">
{currentCall.displayNumber}
</p>
<p className="text-3xl mt-6 text-slate-300">
MENUJU LOKET: <span className="font-bold text-white">{currentCall.counterId}</span>
</p>
</div>
) : (
<p className="text-slate-500 text-xl">Menunggu Panggilan Berikutnya...</p>
)}
</div>
);
}
Catatan Skalabilitas: Mengatasi Keterbatasan Multi-Instance
Jika aplikasi Anda berkembang dan mulai menggunakan multi-instance server (misalnya menggunakan Kubernetes, PM2 Cluster, atau Vercel Serverless), Anda akan menghadapi masalah serius dengan EventEmitter.
Masalahnya: EventEmitter beroperasi secara in-process. Artinya, jika petugas memanggil antrian di Instance A, namun Display TV terhubung ke Instance B, maka TV tersebut tidak akan pernah menerima notifikasi karena event hanya berputar di dalam memori Instance A.
Solusinya: Redis Pub/Sub.
Anda harus mengganti EventEmitter lokal dengan sistem pub/sub eksternal. Alurnya menjadi seperti ini:
- Operator mengirim request ke
/api/queue/call→ Server mempublikasikan pesan ke channel Redis'queue_channel'. - Setiap instance server yang menjalankan SSE stream berlangganan (subscribe) ke channel Redis
'queue_channel'. - Saat Redis menerima pesan, semua instance server akan meneruskannya ke client SSE masing-masing.
Keuntungan menggunakan Redis adalah Anda tidak perlu mengubah kode di sisi frontend (Display TV), karena format data yang dikirim tetap sama.
Kesimpulan
SSE adalah solusi yang elegan untuk kebutuhan real-time satu arah. Dengan implementasi yang tepat, Anda bisa mengurangi beban server, menyederhanakan kode, dan meningkatkan stabilitas aplikasi. Untuk sistem antrian, notifikasi dashboard, atau streaming respons AI, SSE adalah pilihan yang jauh lebih rasional dibandingkan WebSocket.
Solusi yang relevan
Jasa Pembuatan Website
Pembuatan website custom yang cepat, modern, dan siap jualan.
Related Articles

Panduan Lengkap Perulangan For di Golang: Pengganti While Loop
Bingung mencari while loop di Golang? Ternyata Go hanya menggunakan satu kata kunci 'for' untuk semua jenis perulangan. Pelajari cara menguasainya di sini!

Belajar Struktur Dasar Golang untuk Pemula: Panduan Lengkap
Ingin belajar Golang tapi bingung mulai dari mana? Bedah tuntas struktur dasar kode Go, mulai dari package main, import, hingga fungsi utama dalam panduan lengkap untuk pemula ini.

Cara Website Bisnis Mudah Ditemukan di Google (SEO)
Website sepi pengunjung? Pelajari cara agar website bisnis Anda mudah ditemukan di halaman pertama Google dengan strategi SEO dasar yang ampuh dan praktis.

How to Find Quality Daycare: Tools, Standards & Tips
Finding the perfect daycare is a balance of safety, cost, and quality. Learn how to use modern digital tools and regulatory standards to secure the best early childhood environment for your child.
Dapatkan Artikel Terbaru!
Berlangganan newsletter kami untuk mendapatkan tips dan insight menarik langsung ke inbox Anda.
Kami tidak akan pernah membagikan email Anda (No Spam).