Supabase Realtime Lab
Supabase Realtime Home Lab Self-hosted PostgreSQL WebSocket Subscription Broadcast Presence RLS Edge Functions Chat Dashboard Collaboration
| Feature | Protocol | Use Case | Latency | Scale |
|---|---|---|---|---|
| Postgres Changes | WebSocket + LISTEN/NOTIFY | Live data updates | 50-200ms | 10K connections |
| Broadcast | WebSocket | Chat, cursor sharing | 10-50ms | 100K messages/s |
| Presence | WebSocket + CRDT | Online users, typing | 100-500ms | 10K users/room |
| Edge Functions | Deno Deploy | Server-side logic | 5-50ms | Auto-scale |
| Auth | JWT + GoTrue | User authentication | 50-200ms | Unlimited |
Self-hosted Setup
# === Supabase Self-hosted with Docker ===
# Clone and setup
# git clone --depth 1 https://github.com/supabase/supabase
# cd supabase/docker
# cp .env.example .env
# Edit .env — Important settings
# POSTGRES_PASSWORD=your-super-secret-password
# JWT_SECRET=your-super-secret-jwt-token-at-least-32-chars
# ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# DASHBOARD_USERNAME=admin
# DASHBOARD_PASSWORD=admin123
# SITE_URL=http://localhost:3000
# Start all services
# docker compose up -d
# Services started:
# - PostgreSQL :5432
# - Supabase Studio :3000 (Dashboard)
# - Kong Gateway :8000 (API)
# - GoTrue :9999 (Auth)
# - PostgREST :3001 (REST API)
# - Realtime :4000 (WebSocket)
# - Storage :5000 (File Storage)
# - Meta :8080 (Metadata)
# Create table with Realtime enabled
# CREATE TABLE messages (
# id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
# user_id UUID REFERENCES auth.users NOT NULL,
# content TEXT NOT NULL,
# room TEXT NOT NULL DEFAULT 'general',
# created_at TIMESTAMPTZ DEFAULT NOW()
# );
#
# ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
#
# -- Enable Realtime
# ALTER PUBLICATION supabase_realtime ADD TABLE messages;
from dataclasses import dataclass
@dataclass
class ServiceConfig:
service: str
image: str
port: int
resource: str
purpose: str
services = [
ServiceConfig("PostgreSQL", "supabase/postgres", 5432, "2 CPU 4GB RAM", "Database"),
ServiceConfig("Realtime", "supabase/realtime", 4000, "1 CPU 1GB RAM", "WebSocket server"),
ServiceConfig("PostgREST", "postgrest/postgrest", 3001, "0.5 CPU 512MB", "Auto REST API"),
ServiceConfig("GoTrue", "supabase/gotrue", 9999, "0.5 CPU 512MB", "Authentication"),
ServiceConfig("Kong", "kong", 8000, "1 CPU 1GB RAM", "API Gateway"),
ServiceConfig("Studio", "supabase/studio", 3000, "1 CPU 1GB RAM", "Dashboard UI"),
ServiceConfig("Storage", "supabase/storage-api", 5000, "0.5 CPU 512MB", "File storage"),
]
print("=== Supabase Services ===")
for s in services:
print(f" [{s.service}] Port: {s.port} | Image: {s.image}")
print(f" Resource: {s.resource} | Purpose: {s.purpose}")
Realtime Client Code
# === Realtime Subscription Examples ===
# JavaScript Client
# import { createClient } from '@supabase/supabase-js'
#
# const supabase = createClient(
# 'http://localhost:8000',
# 'your-anon-key'
# )
#
# // 1. Postgres Changes — Listen to INSERT
# const channel = supabase
# .channel('messages-channel')
# .on('postgres_changes',
# { event: 'INSERT', schema: 'public', table: 'messages',
# filter: 'room=eq.general' },
# (payload) => {
# console.log('New message:', payload.new)
# addMessageToUI(payload.new)
# }
# )
# .subscribe()
#
# // 2. Broadcast — Send/receive custom events
# const broadcastChannel = supabase
# .channel('cursor-room')
# .on('broadcast', { event: 'cursor-move' }, (payload) => {
# moveCursor(payload.payload.x, payload.payload.y, payload.payload.user)
# })
# .subscribe()
#
# // Send cursor position
# broadcastChannel.send({
# type: 'broadcast',
# event: 'cursor-move',
# payload: { x: 100, y: 200, user: 'alice' }
# })
#
# // 3. Presence — Track online users
# const presenceChannel = supabase
# .channel('online-users')
# .on('presence', { event: 'sync' }, () => {
# const state = presenceChannel.presenceState()
# updateOnlineUsers(Object.keys(state))
# })
# .subscribe(async (status) => {
# if (status === 'SUBSCRIBED') {
# await presenceChannel.track({
# user: 'alice', online_at: new Date().toISOString()
# })
# }
# })
# Python Client
# from supabase import create_client
#
# supabase = create_client("http://localhost:8000", "your-anon-key")
#
# def handle_change(payload):
# print(f"Change: {payload}")
#
# channel = supabase.channel("messages")
# channel.on_postgres_changes(
# event="INSERT",
# schema="public",
# table="messages",
# callback=handle_change
# ).subscribe()
@dataclass
class RealtimeFeature:
feature: str
event_types: str
use_case: str
example: str
features = [
RealtimeFeature("Postgres Changes", "INSERT UPDATE DELETE",
"Live data sync, dashboard updates",
"Chat messages, order status, stock prices"),
RealtimeFeature("Broadcast", "Custom events",
"Peer-to-peer messaging, no persistence",
"Cursor sharing, typing indicator, game state"),
RealtimeFeature("Presence", "sync join leave",
"Track online users, user state",
"Online indicators, collaborative editing"),
]
print("\n=== Realtime Features ===")
for f in features:
print(f" [{f.feature}] Events: {f.event_types}")
print(f" Use case: {f.use_case}")
print(f" Example: {f.example}")
RLS and Security
# === Row Level Security Policies ===
# -- Users can read messages in rooms they belong to
# CREATE POLICY "Users can read room messages"
# ON messages FOR SELECT
# USING (
# room IN (
# SELECT room_id FROM room_members
# WHERE user_id = auth.uid()
# )
# );
#
# -- Users can insert their own messages
# CREATE POLICY "Users can insert own messages"
# ON messages FOR INSERT
# WITH CHECK (auth.uid() = user_id);
#
# -- Users can update their own messages
# CREATE POLICY "Users can update own messages"
# ON messages FOR UPDATE
# USING (auth.uid() = user_id);
#
# -- Users can delete their own messages
# CREATE POLICY "Users can delete own messages"
# ON messages FOR DELETE
# USING (auth.uid() = user_id);
#
# -- Public read for announcements
# CREATE POLICY "Anyone can read announcements"
# ON announcements FOR SELECT
# USING (true);
# Edge Function — Server-side validation
# // supabase/functions/validate-message/index.ts
# import { serve } from 'https://deno.land/std/http/server.ts'
# import { createClient } from 'https://esm.sh/@supabase/supabase-js'
#
# serve(async (req) => {
# const { content, room } = await req.json()
# if (content.length > 1000) {
# return new Response('Message too long', { status: 400 })
# }
# // Moderate content
# if (containsBadWords(content)) {
# return new Response('Inappropriate content', { status: 400 })
# }
# const supabase = createClient(
# Deno.env.get('SUPABASE_URL'),
# Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')
# )
# const { data, error } = await supabase
# .from('messages')
# .insert({ content, room, user_id: req.auth.uid })
# return new Response(JSON.stringify(data))
# })
@dataclass
class SecurityCheck:
check: str
status: str
fix: str
checklist = [
SecurityCheck("RLS enabled on all tables", "REQUIRED",
"ALTER TABLE x ENABLE ROW LEVEL SECURITY"),
SecurityCheck("RLS policies defined", "REQUIRED",
"CREATE POLICY for SELECT INSERT UPDATE DELETE"),
SecurityCheck("JWT Secret rotated", "RECOMMENDED",
"Change JWT_SECRET in .env, restart services"),
SecurityCheck("Service Role Key protected", "REQUIRED",
"Never expose in client code, use only server-side"),
SecurityCheck("Realtime filtered by RLS", "AUTOMATIC",
"Realtime respects RLS policies automatically"),
SecurityCheck("Rate limiting configured", "RECOMMENDED",
"Kong rate-limiting plugin, 100 req/min per user"),
SecurityCheck("HTTPS enabled", "REQUIRED for production",
"Use reverse proxy with SSL certificate"),
]
print("Security Checklist:")
for c in checklist:
print(f" [{c.status}] {c.check}")
print(f" Fix: {c.fix}")
เคล็ดลับ
- RLS: เปิด RLS ทุก Table ก่อน Enable Realtime ป้องกัน Data Leak
- Filter: ใช้ Filter ใน Subscription ฟังเฉพาะข้อมูลที่ต้องการ
- Presence: อย่าเก็บข้อมูลมากใน Presence State เก็บเฉพาะที่จำเป็น
- Unsubscribe: อย่าลืม Unsubscribe เมื่อ Component Unmount
- Backup: ตั้ง pg_dump Backup PostgreSQL ทุกวัน
Supabase Realtime คืออะไร
Real-time Client Database PostgreSQL LISTEN NOTIFY WebSocket Postgres Changes Broadcast Presence Chat Dashboard Collaboration Notification
ตั้ง Self-hosted อย่างไร
Docker Compose PostgreSQL Kong GoTrue PostgREST Realtime Storage Studio .env JWT Secret API Keys Dashboard localhost:3000 Project Table RLS
Realtime Subscription ใช้อย่างไร
Client Library Channel postgres_changes INSERT UPDATE DELETE Broadcast Message Presence Online subscribe Filter Table Column RLS Policy
Row Level Security ตั้งอย่างไร
ENABLE ROW LEVEL SECURITY Policy SELECT INSERT UPDATE DELETE auth.uid() user_id Public Policy ทดสอบ User ต่างกัน
สรุป
Supabase Realtime Home Lab Docker Self-hosted PostgreSQL WebSocket Subscription Broadcast Presence RLS Edge Functions Security Chat Dashboard Production
