Room adalah kelompok koneksi di server, supaya server bisa mengirim event ke sebagian client saja. Namespace adalah jalur terpisah di satu server Socket.IO, dengan aturan masuk, handler, dan room-nya sendiri. Bayangin Laundry Bu Rini punya dua pintu: pintu depan untuk pelanggan dan pintu belakang khusus staf, masing-masing dengan penjaga sendiri (namespace). Di ruang pelanggan ada kotak surat per order: kabar untuk order #12 dimasukkan ke kotak #12, dan cuma yang memegang kunci kotak itu yang membacanya (room). Kuncinya dibagikan petugas, pelanggan gak bisa bikin kunci sendiri.
🧪 Tebakan cepat: Supaya HP Pak Darto menerima kabar order #12, kode di browser-nya memanggil
socket.join('order:12'). Apa yang terjadi? Jawabannya ada di bagian "Room cuma ada di server".
Room: kelompok koneksi di server
Server realtime di port 3000 (socket.io 4.8.4):
io.on('connection', (socket) => {
socket.on('ikuti', (orderId, ack) => {
// di aplikasi sungguhan: cek dulu HP ini memang pemilik order itu
socket.join(`order:${orderId}`)
ack({ ok: true })
})
socket.on('berhenti', (orderId) => socket.leave(`order:${orderId}`))
})
// saat Mas Eko menandai #12 Siap diambil:
io.to('order:12').emit('order:status', { id: 12, status: 'Siap diambil', seq: 4 })
HP Pak Darto:
const socket = io('http://localhost:3000')
socket.on('connect', () => {
socket.emit('ikuti', 12, (res) => console.log(res)) // { ok: true }
})
socket.on('order:status', (e) => tampilkan(e))
socket.join(nama)dansocket.leave(nama)memasukkan dan mengeluarkan satu koneksi. Room dibuat saat anggota pertama masuk dan dibuang otomatis saat kosong (dicoba).io.to('order:12').emit(...)mengirim ke semua anggota room itu. HP Mbak Wulan di roomorder:13gak menerima apa-apa.- Saat koneksi putus, socket keluar dari semua room-nya. Reconnect membuat socket baru dengan id baru yang belum ada di room mana pun. Karena itu
ikutidikirim di dalamsocket.on('connect', ...): handler itu jalan lagi setiap kali tersambung ulang.
Room cuma ada di server
Client Socket.IO gak punya join. Dicoba dengan socket.io-client 4.8.4: socket.join('order:12') di browser melempar TypeError: socket.join is not a function. Itu jawaban tebakannya. Client hanya bisa meminta lewat event (ikuti), dan server yang memutuskan. Ini bagus untuk keamanan: server bisa memeriksa dulu, supaya Mbak Wulan gak bisa ikut room order Pak Darto.
Setiap socket juga otomatis masuk room bernama id-nya sendiri. Begitu tersambung, socket.rooms berisi Set { 'VyInQSLHhBlqqnWFAAAB' }, jadi io.to(socket.id).emit(...) mengirim ke satu koneksi saja.
Mengirim ke siapa
| Kode di server | Yang menerima |
|---|---|
io.emit(...) |
semua socket di namespace / |
io.to('order:12').emit(...) |
semua anggota room, termasuk pengirimnya kalau ia anggota |
socket.to('order:12').emit(...) |
anggota room, kecuali socket itu sendiri |
socket.broadcast.emit(...) |
semua di namespace-nya, kecuali socket itu sendiri |
io.to(socketId).emit(...) |
satu koneksi |
io.to('order:12').to('order:13').emit(...) |
gabungan dua room; yang ada di keduanya menerima sekali |
Semuanya dicoba dengan socket.io 4.8.4.
Namespace: pintu terpisah untuk staf
Bu Rini dan Mas Eko memakai namespace /staff. Pelanggan tetap di /.
const staf = io.of('/staff')
staf.use(async (socket, next) => {
const ok = await cekTokenStaf(socket.handshake.auth.token) // diperiksa server
if (!ok) return next(new Error('Bukan staf'))
next()
})
staf.on('connection', (socket) => {
socket.on('order:set-status', (d, ack) => {
// simpan ke database dulu, lalu:
io.to(`order:${d.id}`).emit('order:status', d) // HP pelanggan, namespace /
socket.broadcast.emit('order:status', d) // staf lain di /staff
ack({ ok: true })
})
})
Tablet Mas Eko tersambung dengan io('http://localhost:3000/staff', { auth: { token } }).
- Namespace adalah path di URL (
/staff). Yang bawaan bernama/. - Handler, middleware (
use), dan room-nya terpisah. Roomorder:12di/dan di/staffadalah dua room berbeda, danio.emitcuma sampai ke/. - Dicoba: HP yang membuka
/stafftanpa token yang benar menerimaconnect_errordenganerr.message"Bukan staf", dan gak pernah masuk. - Satu halaman yang membuka
/dan/stafftetap memakai satu koneksi WebSocket. Paketnya diberi awalan namespace:42/staff,["order:status",{...}](dicoba).
Kapan room, kapan namespace
- Room untuk kelompok yang berubah-ubah saat aplikasi jalan: per order, per chat, per user. Murah, ribuan room gak masalah.
- Namespace untuk pembagian tetap yang sudah dikenal sejak awal, dengan aturan masuk atau handler berbeda: pelanggan dan staf. Jumlahnya sedikit.
Singkatnya, namespace memisahkan logic di level koneksi, dan room mengelompokkan client di dalam namespace yang sama.
Tanpa Socket.IO: room dengan ws biasa
WebSocket biasa gak punya room. Kamu buat sendiri dengan Map (dicoba dengan ws 8.22.0):
import { WebSocketServer, WebSocket } from 'ws'
const wss = new WebSocketServer({ port: 3000 })
const rooms = new Map() // 'order:12' -> Set berisi socket
function join(ws, room) {
if (!rooms.has(room)) rooms.set(room, new Set())
rooms.get(room).add(ws)
}
function leave(ws, room) {
const set = rooms.get(room)
if (!set) return
set.delete(ws)
if (set.size === 0) rooms.delete(room) // room kosong dibuang
}
function toRoom(room, pesan, kecuali) {
const teks = JSON.stringify(pesan)
for (const ws of rooms.get(room) ?? []) {
if (ws !== kecuali && ws.readyState === WebSocket.OPEN) ws.send(teks)
}
}
wss.on('connection', (ws) => {
ws.on('message', (data) => {
const msg = JSON.parse(data)
if (msg.type === 'join') join(ws, `order:${msg.id}`)
})
ws.on('close', () => {
for (const room of [...rooms.keys()]) leave(ws, room)
})
})
roomsmemetakan nama room keSetberisi koneksi.toRoommengirim ke tiap anggota, dengankecualiuntuk menirusocket.to.- Handler
closewajib: tanpa itu, socket yang sudah mati tetap tersimpan, memorinya gak pernah lepas. - Namespace di ws biasa: path URL berbeda (
ws://localhost:3000/staff) denganMapdan pemeriksaan sendiri.
⚠️ Jebakan yang sering ketemu
- Memanggil
joindi browser. Gak ada. Kirim event, server yang memasukkan. - Join cuma sekali. Setelah reconnect, socket baru belum ada di room. Kirim
ikutidi handlerconnect. io.topadahal maksudnyasocket.to. Pengirim ikut menerima pesannya sendiri, dan chat-nya tampil dobel.- Percaya
authdari client.auth: { role: 'admin' }cuma teks yang dikirim browser. Periksa token di middleware.
TL;DR: Room adalah kelompok koneksi yang cuma ada di server: socket.join('order:12') dan leave dipanggil server, io.to('order:12').emit(...) mengirim ke anggotanya, socket.to(...) dan socket.broadcast mengecualikan pengirim, dan tiap socket punya room bernama id-nya sendiri. Room dibuang saat kosong, dan socket keluar dari semua room saat putus, jadi minta join lagi di handler connect. Namespace (io.of('/staff')) adalah jalur terpisah dengan middleware, handler, dan room sendiri, berbagi satu koneksi WebSocket. Namespace memisahkan logic di level koneksi; room mengelompokkan client di dalam satu namespace. Dengan ws biasa, room adalah Map dari nama ke Set socket yang kamu rawat sendiri.