supabase-realtime

Nice-Wolf-Studio's avatarfrom Nice-Wolf-Studio

Subscribe to realtime changes in Supabase using WebSocket connections. Use for listening to database changes, presence tracking, and broadcast messaging.

0stars🔀0forks📁View on GitHub🕐Updated Oct 20, 2025

When & Why to Use This Skill

This Claude skill provides comprehensive guidance and practical bash-based patterns for integrating with Supabase Realtime via WebSocket connections. It enables developers to monitor live database changes (INSERT, UPDATE, DELETE), implement presence tracking for collaborative features, and manage broadcast messaging for instant communication, all through optimized command-line tools like websocat and jq.

Use Cases

  • Real-time Database Auditing: Automatically listen to and log every change in sensitive tables (e.g., 'orders' or 'users') to monitor system activity in real-time.
  • Live Notification Systems: Trigger immediate system alerts or external notifications whenever a specific event occurs in the database, such as a new high-priority support ticket.
  • User Presence Monitoring: Track the online/offline status of users within specific application 'rooms' or 'lobbies' to power collaborative tools and dashboards.
  • Cross-Instance Messaging: Use the broadcast feature to send instant updates or configuration changes across multiple distributed application instances without a complex backend.
  • Automated Data Synchronization: Build lightweight bash scripts that detect database updates and immediately sync data to local caches or third-party APIs.
namesupabase-realtime
descriptionSubscribe to realtime changes in Supabase using WebSocket connections. Use for listening to database changes, presence tracking, and broadcast messaging.

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:

  1. Go to Database > Replication
  2. Enable replication for tables you want to listen to
  3. 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

supabase-realtime – AI Agent Skills | Claude Skills