Bundle Real-Time: Pusher vs PartyKit vs Supabase Realtime
Tiga real-time provider untuk SaaS Indonesia. Saya pakai Pusher 18 bulan, PartyKit 10 bulan, Supabase Realtime 24 bulan. Bundle recipe + cost + decision matrix.
TL;DR
- Supabase Realtime: Gratis di Pro plan, Postgres CDC native. Conditional Recommend default untuk SaaS yang sudah pakai Supabase.
- Pusher: Matang, channel powerful, $49+/mo. Conditional Recommend untuk chat / notification standalone.
- PartyKit: Edge-native, stateful, latency Indonesia excellent. Conditional Recommend untuk collaborative app.
- Verdict: pilih berdasarkan use case (data sync, chat, collaborative).
Konteks
Saya pakai ketiga real-time provider di 9 project SMB Indonesia 2023-2026:
- Supabase Realtime (6 project): SaaS dental (appointment status real-time), HR SaaS (notification), ecommerce (inventory sync), chatbot (message stream), AI assistant (streaming response), admin panel (live update)
- Pusher (2 project): klien legacy chat app (sebelum Supabase Realtime matang), klien notification heavy
- PartyKit (1 project): klien collaborative whiteboard Q1 2026
Total 24 bulan compare.
Pricing (Juni 2026)
Supabase Realtime
- Free: 200 concurrent connection, 2 juta message/bulan
- Pro ($25/mo = Rp 400rb): 500 concurrent, 5 juta message/bulan
- Team ($599/mo): 10K concurrent, 100 juta message/bulan
Pusher
- Sandbox ($0): 100 connection, 200K message/hari
- Startup ($49/mo = Rp 784rb): 500 connection, 1 juta message/hari
- Pro ($99/mo): 2K connection
- Business ($499/mo): 10K connection
PartyKit
- Free: 50K request/hari, 100 connection
- Pro ($20/mo = Rp 320rb): unlimited connection, 1M request/bulan
- Scale: $0.50/M request setelah 1M
Cost estimate untuk SaaS 5K MAU
Assumption: 200 concurrent real-time user, 50 message/menit average.
| Provider | Monthly cost |
|---|---|
| Supabase Realtime (Pro plan already) | Rp 0 extra |
| Pusher Startup | Rp 784rb |
| PartyKit Pro | Rp 320rb + usage |
Supabase Realtime paling hemat untuk SaaS yang sudah pakai Supabase stack.
Feature comparison
| Feature | Supabase Realtime | Pusher | PartyKit |
|---|---|---|---|
| WebSocket | Yes | Yes | Yes (edge) |
| Broadcast (pub/sub) | Yes | Yes (channel) | Yes (room) |
| Postgres CDC | Yes (native) | No | No |
| Presence (online users) | Yes | Yes | Yes |
| Private channel + auth | Yes (via JWT) | Yes (auth endpoint) | Yes (custom) |
| Stateful (per room) | Limited | Limited | Yes (Durable Objects) |
| Multi-region | Limited | Yes (multiple) | Yes (Cloudflare global) |
| Indonesia region | Singapore | Singapore/HK | Jakarta PoP (CF) |
| Edge runtime | No | No | Yes |
| Self-host | Yes (open-source) | No | Yes (PartyServer) |
| Pricing model | Tier-based | Tier + usage | Usage-based |
Trade-off
Supabase Realtime
Pro: Gratis di Pro plan (included). Postgres CDC = data change auto-broadcast, no extra code untuk sync. Auth integrate dengan Supabase Auth. Setup minimal (subscribe via JS SDK). RLS-aware broadcasting.
Con: Scaling beyond 500 concurrent butuh tier upgrade ($599+). Latency Singapore (25-45ms Jakarta). Tidak ideal untuk ultra-high-throughput chat (50K+ message/min). Stateful operation per channel limited.
Pusher
Pro: Matang (10+ tahun production). Channel + presence + private channel API powerful. Documentation excellent. SDK lengkap (JS, Swift, Kotlin, Flutter, React Native). Multi-region untuk latency.
Con: Tier termurah $49/mo (no free tier production-grade). Tidak Postgres-aware (butuh manual trigger broadcast). Vendor lock-in moderate. Scaling cost steep di high concurrent.
PartyKit
Pro: Edge-native — latency Indonesia excellent (Jakarta PoP). Stateful per room via Durable Objects. Multi-region by default (Cloudflare global). Open-source (bisa self-host). API simple. Cocok untuk collaborative app (multiplayer game, whiteboard, document editing).
Con: Ekosistem masih kecil dibanding Pusher/Supabase. Documentation OK tapi tidak as polished. Pricing model usage-based bisa unpredictable. Tidak ada Postgres CDC equivalent (manual broadcast).
Use case decision matrix
| Use case | Recommended |
|---|---|
| Notification real-time (push to user) | Supabase Realtime atau Pusher |
| Chat 1-on-1 atau group small | Supabase Realtime |
| Chat heavy (Discord-like, 1000+ message/menit) | Pusher Business atau self-host |
| Collaborative editing (Figma-like) | PartyKit |
| Multiplayer game (real-time state) | PartyKit |
| Data sync (Postgres change → UI update) | Supabase Realtime (CDC native) |
| Live dashboard (metric streaming) | Supabase Realtime |
| Presence (who’s online) | Semua support, pick based on other need |
| Customer-facing chat support | Pusher (matang) atau Supabase |
| Internal tool real-time | Supabase Realtime |
Setup recipe (per provider)
Supabase Realtime (15 menit setup)
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
// 1. Listen Postgres change
const channel = supabase
.channel('appointments-channel')
.on(
'postgres_changes',
{ event: '*', schema: 'public', table: 'appointments' },
(payload) => {
console.log('Change received', payload);
// Update UI
}
)
.subscribe();
// 2. Broadcast custom message
channel.send({
type: 'broadcast',
event: 'typing',
payload: { user: 'asti' },
});
// 3. Presence
channel.on('presence', { event: 'sync' }, () => {
const state = channel.presenceState();
console.log('Online users', state);
});
Pusher (30 menit setup)
import Pusher from 'pusher-js';
const pusher = new Pusher(PUSHER_KEY, { cluster: 'ap1' });
// Subscribe to channel
const channel = pusher.subscribe('notifications');
channel.bind('new-message', (data) => {
console.log('Received', data);
});
// Server side (Node):
import Pusher from 'pusher';
const pusherServer = new Pusher({
appId: APP_ID, key: KEY, secret: SECRET, cluster: 'ap1',
});
pusherServer.trigger('notifications', 'new-message', { text: 'Hello' });
Plus setup auth endpoint untuk private channel:
// /api/pusher/auth
export async function POST(req: Request) {
const { socket_id, channel_name } = await req.formData();
const user = await getUser();
const auth = pusherServer.authorizeChannel(socket_id, channel_name, {
user_id: user.id,
});
return Response.json(auth);
}
PartyKit (45 menit setup)
bun add partykit partysocket
bunx partykit init
// party/index.ts (server)
import type * as Party from 'partykit/server';
export default class Server implements Party.Server {
constructor(readonly room: Party.Room) {}
onMessage(message: string, sender: Party.Connection) {
this.room.broadcast(message, [sender.id]);
}
onConnect(conn: Party.Connection) {
console.log(`User ${conn.id} connected`);
}
}
// client
import PartySocket from 'partysocket';
const socket = new PartySocket({
host: 'my-app.partykit.dev',
room: 'lobby',
});
socket.addEventListener('message', (e) => {
console.log(e.data);
});
socket.send(JSON.stringify({ type: 'chat', text: 'Hello' }));
Deploy:
bunx partykit deploy
Performance (production data)
Latency Jakarta median
| Provider | Subscribe setup | Message roundtrip |
|---|---|---|
| Supabase Realtime (Singapore) | 250-400ms | 60-150ms |
| Pusher (ap1 Mumbai) | 320-500ms | 80-200ms |
| PartyKit (CF Jakarta PoP) | 80-150ms | 25-80ms |
PartyKit menang clear untuk customer Indonesia karena CF edge.
Throughput
| Provider | Message/sec sustained per channel |
|---|---|
| Supabase Realtime | ~200/sec |
| Pusher Startup | ~500/sec |
| PartyKit | ~1000/sec (per Durable Object) |
PartyKit best per-room throughput.
Konteks Indonesia
SaaS dental Jakarta (Supabase Realtime): appointment status sync ke dashboard staff klinik. 12 concurrent staff user. Postgres CDC = update DB → semua dashboard sync dalam < 200ms. Bill: Rp 0 extra (included Pro plan).
HR SaaS Jakarta (Supabase Realtime + Pusher untuk notification mobile): dashboard real-time (Supabase) + push notification mobile (Pusher Beams alternatif). Bill: Rp 0 Supabase + Rp 784rb Pusher = Rp 784rb/bulan.
Collaborative whiteboard Tangerang (PartyKit): 50 concurrent user editing canvas. Latency Jakarta 35ms median. Cursor + element sync smooth. Bill: Rp 320rb/bulan + usage Rp 100-300rb = ~Rp 500rb total.
Migration paths
Pusher → Supabase Realtime
Effort: 1-3 minggu. Refactor channel + event ke Postgres CDC atau Broadcast. Auth ke Supabase JWT. Worth jika SaaS sudah pakai Supabase + bill Pusher > Rp 500rb/bulan.
Supabase Realtime → PartyKit
Effort: 2-4 minggu. Untuk app yang grow ke collaborative complex. Refactor data sync pattern (CDC → manual broadcast via Durable Object). Worth jika latency Indonesia critical atau butuh stateful per room.
PartyKit → Custom WebSocket
Effort: 4-8 minggu. Self-host WebSocket server (Hono + Bun + Redis). Worth jika scale > 10K concurrent dan cost PartyKit > Rp 5 juta/bulan.
Verdict
Conditional Recommend untuk ketiga:
Pakai Supabase Realtime jika:
- SaaS sudah pakai Supabase stack
- Data sync workload (Postgres change → UI)
- Notification + presence ringan
- Budget tight (no extra cost di Pro plan)
Pakai Pusher jika:
- Chat / notification standalone (tidak butuh Postgres CDC)
- Scaling > 500 concurrent
- Channel + presence API powerful butuh
- Mobile SDK matang priority
Pakai PartyKit jika:
- Collaborative app (whiteboard, multiplayer, document editor)
- Latency Indonesia critical (Jakarta PoP)
- Stateful per room logic kompleks
- Edge-first stack
Skip jika: app Anda tidak butuh real-time (polling 30-60 detik cukup untuk most use case).
Untuk Supabase stack lengkap: Bundle AI SaaS Claude + Supabase + Vercel. Untuk cache + queue Redis comparison: Redis vs Memcached vs Dragonfly.
Ditulis oleh Asti Larasati