Supabase Realtime Overview
This skill provides guidance for working with Supabase Realtime features. Realtime allows you to listen to database changes, broadcast messages, and track presence using WebSocket connections.
Note: Realtime operations require WebSocket support, which is more complex in bash. This skill focuses on practical patterns and examples using available tools.
Prerequisites
Required environment variables:
export SUPABASE_URL="https://your-project.supabase.co" export SUPABASE_KEY="your-anon-or-service-role-key"
Additional tools:
websocat or wscat for WebSocket connections jq for JSON processing
Install websocat:
macOS
brew install websocat
Linux
wget https://github.com/vi/websocat/releases/download/v1.12.0/websocat.x86_64-unknown-linux-musl chmod +x websocat.x86_64-unknown-linux-musl sudo mv websocat.x86_64-unknown-linux-musl /usr/local/bin/websocat
WebSocket Connection
Connect to Supabase Realtime:
SUPABASE_URL="https://your-project.supabase.co" SUPABASE_KEY="your-anon-key"
Extract WebSocket URL (replace https:// with wss://)
WS_URL=$(echo "$SUPABASE_URL" | sed 's/https:/wss:/')
Connect to realtime
websocat "${WS_URL}/realtime/v1/websocket?apikey=${SUPABASE_KEY}&vsn=1.0.0"
Database Change Subscriptions Subscribe to Table Changes
Listen to all changes on a table:
!/bin/bash
SUPABASE_URL="https://your-project.supabase.co" SUPABASE_KEY="your-anon-key" WS_URL=$(echo "$SUPABASE_URL" | sed 's/https:/wss:/')
Create subscription message
SUB_MESSAGE='{ "topic": "realtime:public:users", "event": "phx_join", "payload": {}, "ref": "1" }'
Connect and subscribe
echo "$SUB_MESSAGE" | websocat "${WS_URL}/realtime/v1/websocket?apikey=${SUPABASE_KEY}&vsn=1.0.0"
Subscribe to specific events:
Listen for INSERT events only
SUB_MESSAGE='{ "topic": "realtime:public:users", "event": "phx_join", "payload": { "config": { "postgres_changes": [ { "event": "INSERT", "schema": "public", "table": "users" } ] } }, "ref": "1" }'
echo "$SUB_MESSAGE" | websocat "${WS_URL}/realtime/v1/websocket?apikey=${SUPABASE_KEY}&vsn=1.0.0"
Subscribe to UPDATE events:
SUB_MESSAGE='{ "topic": "realtime:public:products", "event": "phx_join", "payload": { "config": { "postgres_changes": [ { "event": "UPDATE", "schema": "public", "table": "products" } ] } }, "ref": "1" }'
Subscribe to DELETE events:
SUB_MESSAGE='{ "topic": "realtime:public:posts", "event": "phx_join", "payload": { "config": { "postgres_changes": [ { "event": "DELETE", "schema": "public", "table": "posts" } ] } }, "ref": "1" }'
Subscribe to all events (*, INSERT, UPDATE, DELETE):
SUB_MESSAGE='{ "topic": "realtime:public:orders", "event": "phx_join", "payload": { "config": { "postgres_changes": [ { "event": "*", "schema": "public", "table": "orders" } ] } }, "ref": "1" }'
Filter Subscriptions
Listen to changes matching a filter:
Only listen to changes where status = 'active'
SUB_MESSAGE='{ "topic": "realtime:public:users", "event": "phx_join", "payload": { "config": { "postgres_changes": [ { "event": "*", "schema": "public", "table": "users", "filter": "status=eq.active" } ] } }, "ref": "1" }'
Broadcast Messaging Send Broadcast Message
Broadcast a message to a channel:
!/bin/bash
SUPABASE_URL="https://your-project.supabase.co" SUPABASE_KEY="your-anon-key" WS_URL=$(echo "$SUPABASE_URL" | sed 's/https:/wss:/')
Join channel first
JOIN_MESSAGE='{ "topic": "realtime:chat-room-1", "event": "phx_join", "payload": { "config": { "broadcast": { "self": true } } }, "ref": "1" }'
Broadcast message
BROADCAST_MESSAGE='{ "topic": "realtime:chat-room-1", "event": "broadcast", "payload": { "type": "message", "event": "new_message", "payload": { "user": "Alice", "message": "Hello, World!" } }, "ref": "2" }'
Send messages
{ echo "$JOIN_MESSAGE" sleep 1 echo "$BROADCAST_MESSAGE" } | websocat "${WS_URL}/realtime/v1/websocket?apikey=${SUPABASE_KEY}&vsn=1.0.0"
Listen to Broadcast Messages
Receive broadcast messages:
Join channel and listen
JOIN_MESSAGE='{ "topic": "realtime:chat-room-1", "event": "phx_join", "payload": { "config": { "broadcast": { "self": false } } }, "ref": "1" }'
echo "$JOIN_MESSAGE" | websocat "${WS_URL}/realtime/v1/websocket?apikey=${SUPABASE_KEY}&vsn=1.0.0"
Presence Tracking Track Presence
Join channel with presence:
PRESENCE_MESSAGE='{ "topic": "realtime:lobby", "event": "phx_join", "payload": { "config": { "presence": { "key": "user-123" } } }, "ref": "1" }'
Track presence state
TRACK_MESSAGE='{ "topic": "realtime:lobby", "event": "presence", "payload": { "type": "presence", "event": "track", "payload": { "user_id": "123", "username": "Alice", "status": "online" } }, "ref": "2" }'
Untrack Presence
Leave presence:
UNTRACK_MESSAGE='{ "topic": "realtime:lobby", "event": "presence", "payload": { "type": "presence", "event": "untrack" }, "ref": "3" }'
Practical Patterns Continuous Listener Script
!/bin/bash
listen-to-changes.sh
SUPABASE_URL="https://your-project.supabase.co" SUPABASE_KEY="your-anon-key" WS_URL=$(echo "$SUPABASE_URL" | sed 's/https:/wss:/') TABLE="users"
echo "Listening for changes on $TABLE table..."
Subscribe to changes
SUB_MESSAGE='{ "topic": "realtime:public:'"$TABLE"'", "event": "phx_join", "payload": { "config": { "postgres_changes": [ { "event": "*", "schema": "public", "table": "'"$TABLE"'" } ] } }, "ref": "1" }'
Listen continuously
echo "$SUB_MESSAGE" | websocat "${WS_URL}/realtime/v1/websocket?apikey=${SUPABASE_KEY}&vsn=1.0.0" | \ while IFS= read -r line; do echo "[$(date '+%Y-%m-%d %H:%M:%S')] $line" | jq '.' done
Process Changes with Handler
!/bin/bash
process-changes.sh
handle_insert() { local record="$1" echo "New record inserted:" echo "$record" | jq '.payload.record'
# Your custom logic here
# Example: Send notification, update cache, etc.
}
handle_update() { local old_record="$1" local new_record="$2" echo "Record updated:" echo "Old: $(echo "$old_record" | jq -c '.')" echo "New: $(echo "$new_record" | jq -c '.')" }
handle_delete() { local record="$1" echo "Record deleted:" echo "$record" | jq '.payload.old_record' }
Listen and process
websocat "${WS_URL}/realtime/v1/websocket?apikey=${SUPABASE_KEY}&vsn=1.0.0" | \ while IFS= read -r line; do event_type=$(echo "$line" | jq -r '.payload.data.type // empty')
case "$event_type" in
"INSERT")
handle_insert "$(echo "$line" | jq '.payload.data')"
;;
"UPDATE")
handle_update \
"$(echo "$line" | jq '.payload.data.old_record')" \
"$(echo "$line" | jq '.payload.data.record')"
;;
"DELETE")
handle_delete "$(echo "$line" | jq '.payload.data')"
;;
esac
done
Multi-Table Listener
!/bin/bash
listen-multiple-tables.sh
TABLES=("users" "posts" "comments")
for table in "${TABLES[@]}"; do ( echo "Starting listener for $table" SUB_MESSAGE='{ "topic": "realtime:public:'"$table"'", "event": "phx_join", "payload": { "config": { "postgres_changes": [{"event": "*", "schema": "public", "table": "'"$table"'"}] } }, "ref": "1" }'
echo "$SUB_MESSAGE" | websocat "${WS_URL}/realtime/v1/websocket?apikey=${SUPABASE_KEY}&vsn=1.0.0" | \
while IFS= read -r line; do
echo "[$table] $line"
done
) &
done
wait
Message Format Subscription Confirmation { "event": "phx_reply", "payload": { "response": { "postgres_changes": [ { "id": "12345", "event": "*", "schema": "public", "table": "users" } ] }, "status": "ok" }, "ref": "1", "topic": "realtime:public:users" }
INSERT Event { "event": "postgres_changes", "payload": { "data": { "commit_timestamp": "2023-01-01T12:00:00Z", "record": { "id": 123, "name": "John Doe", "email": "john@example.com" }, "schema": "public", "table": "users", "type": "INSERT" }, "ids": [12345] }, "topic": "realtime:public:users" }
UPDATE Event { "event": "postgres_changes", "payload": { "data": { "commit_timestamp": "2023-01-01T12:00:00Z", "old_record": { "id": 123, "name": "John Doe" }, "record": { "id": 123, "name": "Jane Doe" }, "schema": "public", "table": "users", "type": "UPDATE" } } }
DELETE Event { "event": "postgres_changes", "payload": { "data": { "commit_timestamp": "2023-01-01T12:00:00Z", "old_record": { "id": 123, "name": "John Doe" }, "schema": "public", "table": "users", "type": "DELETE" } } }
Alternative: REST Polling
For simpler use cases where WebSockets are impractical, consider polling:
!/bin/bash
poll-changes.sh
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
LAST_TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
while true; do # Get records created/updated since last check new_records=$(supabase_get "/rest/v1/users?updated_at=gt.${LAST_TIMESTAMP}&order=updated_at.asc")
if [[ "$new_records" != "[]" ]]; then
echo "New changes detected:"
echo "$new_records" | jq '.'
# Update timestamp
LAST_TIMESTAMP=$(echo "$new_records" | jq -r '.[-1].updated_at')
fi
# Poll every 5 seconds
sleep 5
done
Realtime Configuration
Enable Realtime in Supabase Dashboard:
Go to Database > Replication Enable replication for tables you want to listen to Choose which events to publish (INSERT, UPDATE, DELETE)
Row Level Security: Realtime respects RLS policies. Users only receive changes for rows they have access to.
Limitations WebSocket connections require persistent connection management Bash is not ideal for WebSocket handling (consider Node.js/Python for production) Connection drops require reconnection logic Realtime is subject to connection limits based on your Supabase plan Use Cases
Good for Realtime in bash:
Development/debugging tools Simple monitoring scripts Log streaming Testing realtime functionality
Better in other languages:
Production chat applications Complex presence tracking Multi-channel coordination Auto-reconnection requirements API Documentation
Full Supabase Realtime documentation: https://supabase.com/docs/guides/realtime