Supabase Realtime Subscriptions — Live Data Updates with WebSockets
In this tutorial, you will learn about Supabase Realtime Subscriptions. We cover key concepts, practical examples, and best practices to help you master this topic.
Supabase Realtime uses Websocket connections to listen for PostgreSQL database changes, enabling live updates for collaborative apps, dashboards, and notification systems without polling.
What You'll Learn
By the end of this lesson you will enable realtime on a table, subscribe to INSERT, UPDATE, and DELETE changes, filter events by column values, and use broadcast and presence features.
Why It Matters
Realtime updates transform static web applications into responsive, collaborative experiences. Users see changes instantly as other users or backend processes modify the database.
Real-World Use
DodaZIP uses Supabase Realtime to show file compression progress. When a backend worker updates the processing status in the database, the user's browser receives the update within milliseconds via WebSocket.
flowchart LR
A[User A Browser] -->|Subscribe| R[Realtime Engine]
B[Backend Worker] -->|UPDATE row| DB[(PostgreSQL)]
DB -->|CDC| R
R -->|WebSocket push| A
R -->|WebSocket push| C[User B Browser]
style R fill:#3ecf8e,color:#fff
Enabling Realtime
Realtime must be enabled per table in the Supabase dashboard or via SQL.
-- Enable realtime for a table via SQL
-- This runs on the Supabase database
ALTER PUBLICATION supabase_realtime ADD TABLE processing_jobs;
-- You can also enable in the dashboard:
-- Database > Replication > select table > toggle realtime
-- Verify enabled tables
SELECT * FROM pg_publication_tables
WHERE pubname = 'supabase_realtime';
# enable_realtime.py
# Understanding realtime enablement
def realtime_checklist():
items = [
"Enable realtime for table in Dashboard > Database > Replication",
"OR run: ALTER PUBLICATION supabase_realtime ADD TABLE table_name",
"Enable RLS on the table (realtime respects RLS)",
"Create appropriate RLS policies for realtime access",
"Subscribe from client using the Supabase SDK",
]
print("Realtime Enablement Checklist:")
for item in items:
print(f" [ ] {item}")
realtime_checklist()
Subscribing to Changes
Subscribe to database changes from the client SDK.
# subscribe_changes.py
# Subscribe to database changes
import os
from supabase import create_client, Client
url = os.getenv("SUPABASE_URL", "https://example.supabase.co")
key = os.getenv("SUPABASE_ANON_KEY", "your-anon-key")
supabase: Client = create_client(url, key)
def subscribe_to_changes():
channel = supabase.channel("job-updates")
def handle_insert(payload):
print(f"New job created:")
print(f" Record: {payload['new']}")
def handle_update(payload):
print(f"Job updated:")
print(f" Old: {payload['old']}")
print(f" New: {payload['new']}")
def handle_delete(payload):
print(f"Job deleted:")
print(f" Old: {payload['old']}")
channel.on(
"postgres_changes",
{
"event": "*",
"schema": "public",
"table": "processing_jobs",
},
lambda payload: print(f"Change: {payload}")
).subscribe()
print("Subscribed to processing_jobs changes")
print("Listening for INSERT, UPDATE, DELETE events...")
return channel
subscribe_to_changes()
Filtering Events
Listen for specific events or rows that match conditions.
# filtered_subscription.py
# Subscribe with filters
def subscribe_filtered():
supabase = create_client(
os.getenv("SUPABASE_URL", "https://example.supabase.co"),
os.getenv("SUPABASE_ANON_KEY", "your-anon-key")
)
# Subscribe only to INSERT events
channel_insert = supabase.channel("new-jobs")
channel_insert.on(
"postgres_changes",
{"event": "INSERT", "schema": "public", "table": "processing_jobs"},
lambda p: print(f"New job: {p['new']}")
).subscribe()
# Subscribe with column filter (using PostgreSQL filter)
# Only receive changes where status = 'completed'
channel_completed = supabase.channel("completed-jobs")
channel_completed.on(
"postgres_changes",
{
"event": "UPDATE",
"schema": "public",
"table": "processing_jobs",
"filter": "status=eq.completed"
},
lambda p: print(f"Job completed: {p['new']}")
).subscribe()
print("Filtered subscriptions active:")
print(" 1. All INSERT events on processing_jobs")
print(" 2. UPDATE events where status = 'completed'")
subscribe_filtered()
Broadcast and Presence
Send custom messages and track user presence.
# broadcast_presence.py
# Broadcast and presence features
def broadcast_example():
supabase = create_client(
os.getenv("SUPABASE_URL", "https://example.supabase.co"),
os.getenv("SUPABASE_ANON_KEY", "your-anon-key")
)
# Broadcast a custom message to all channel subscribers
channel = supabase.channel("chat-room")
def handle_broadcast(payload):
print(f"Broadcast received:")
print(f" Event: {payload['event']}")
print(f" Data: {payload}")
channel.on(
"broadcast",
{"event": "cursor-position"},
handle_broadcast
).subscribe()
# Send a broadcast
channel.send_broadcast(
"cursor-position",
{"x": 150, "y": 300, "user": "alice"}
)
print("Broadcast sent: cursor-position { x: 150, y: 300 }")
print()
# Presence tracking
channel.track({"user_id": "alice", "status": "online"})
print("Presence tracked: alice is online")
broadcast_example()
Common Mistakes
Not enabling the table for realtime: Subscribing to a table that is not enabled for realtime silently returns no events.
RLS blocking realtime events: Realtime respects RLS. If the user cannot SELECT a row, they will not receive realtime updates for it.
Not cleaning up subscriptions: Forgetting to unsubscribe when leaving a page causes memory leaks and unnecessary WebSocket connections.
Over-filtering on the server: Heavy server-side filters can cause performance issues. Filter at the client level when possible.
Reconnecting after disconnect: The Supabase SDK auto-reconnects, but you must handle stale data or missed events during the reconnection period.
Practice Questions
How do you enable realtime for a table in Supabase? Use the Dashboard (Database > Replication) or run
ALTER PUBLICATION supabase_realtime ADD TABLE table_name.What events can you subscribe to? INSERT, UPDATE, DELETE, or use
*for all events.How do you filter realtime events by column value? Use the
filterparameter in the subscription options, e.g.,status=eq.completed.What is the difference between broadcast and postgres_changes? Broadcast sends custom messages directly. Postgres_changes listens for actual database row changes.
Challenge: Build a collaborative typing indicator that shows when other users are typing, using presence and broadcast features.
FAQ
Mini Project
Create a realtime dashboard that shows live file processing updates. The dashboard subscribes to processing_jobs changes and displays status updates as they happen.
def realtime_dashboard():
print("Realtime Dashboard initialized")
print("Connected to Supabase Realtime")
print()
events = [
{"id": 1, "file": "report.pdf", "status": "uploading", "progress": 15},
{"id": 1, "file": "report.pdf", "status": "processing", "progress": 47},
{"id": 2, "file": "photos.zip", "status": "uploading", "progress": 5},
{"id": 1, "file": "report.pdf", "status": "compressing", "progress": 78},
{"id": 2, "file": "photos.zip", "status": "processing", "progress": 34},
{"id": 1, "file": "report.pdf", "status": "completed", "progress": 100},
{"id": 2, "file": "photos.zip", "status": "compressing", "progress": 62},
{"id": 2, "file": "photos.zip", "status": "completed", "progress": 100},
]
for event in events:
status_icon = {"uploading": "UP", "processing": "PR", "compressing": "CP", "completed": "DN"}
icon = status_icon.get(event["status"], "??")
print(f" [{icon}] {event['file']:20s} {event['status']:12s} {event['progress']:3d}%")
realtime_dashboard()
What's Next
Next: Storage Buckets for file uploads and management.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro