Supabase Realtime Python Client

repository·main·Indexed 41 days ago

https://github.com/supabase/realtime-py

A Python client (version 2.7.0) for Supabase Realtime that enables low-latency broadcast messaging, presence tracking via CRDTs, and listening to PostgreSQL database changes through Change Data Capture (CDC). It provides both AsyncRealtimeClient for asynchronous applications and SyncRealtimeClient for synchronous blocking calls.

Tokens
7.5K
Snippets
31
Records
34
Agent score
51%

What's inside Supabase Realtime

  1. Use Broadcast to send ephemeral messages

    main

    Broadcast allows sending low-latency, ephemeral messages between clients.

    Configuration Options:

    • ack (bool): If true, the send_broadcast call will resolve once the server acknowledges receipt.
    • self (bool): If true, the client that sent the message will also receive it.
    • private (bool): If true, Row Level Security (RLS) is used to determine connection permissions.

    API Methods:

    • channel.on_broadcast(event, callback): Register a listener for a specific event name.
    • channel.send_broadcast(event, payload): Send a message with a specific event name and JSON payload.
    # Example setup
    channel = client.channel(
        "broadcast-test", {"config": {"broadcast": {"ack": False, "self": False}}}
    )
    
    # Listen for events
    await channel.on_broadcast("some-event", lambda payload: print(payload)).subscribe()
    
    # Send events
    await channel.send_broadcast("some-event", {"hello": "world"})
  2. Use Presence to synchronize state

    main

    Presence allows clients to track and synchronize shared state (like online users) using CRDTs.

    Configuration:

    • Use the presence key in the channel config to specify a key.

    API Methods:

    • channel.on_presence_sync(callback): Triggered when the local presence state is synchronized.
    • channel.on_presence_join(callback): Triggered when new users join the channel.
    • channel.on_presence_leave(callback): Triggered when users leave the channel.
    • channel.track(payload): Updates the current client's presence state.
    • channel.presence_state(): Returns the current synchronized state of all participants.
    channel = client.channel(
        "presence-test",
        {
            "config": {
                "presence": {
                    "key": ""
                }
            }
        }
    )
    
    channel.on_presence_sync(lambda: print("Online users: ", channel.presence_state()))
    channel.on_presence_join(lambda new_presences: print("New users have joined: ", new_presences))
    channel.on_presence_leave(lambda left_presences: print("Users have left: ", left_presences))
    
    await channel.track({ 'user_id': 1 })
  3. Manage and clean up channels

    main

    To prevent memory leaks and unnecessary connections, always clean up channels when they are no longer needed.

    Methods:

    • client.get_channels(): Returns a list of all instantiated channels.
    • await client.remove_channel(channel): Removes a specific channel instance.
    • await client.remove_all_channels(): Removes all currently instantiated channels.
    # Remove a single channel
    channel = client.channel('some-channel-to-remove')
    await channel.subscribe()
    await client.remove_channel(channel)
    
    # Remove all channels
    channel1 = client.channel('a-channel-to-remove')
    channel2 = client.channel('another-channel-to-remove')
    await channel1.subscribe()
    await channel2.subscribe()
    await client.remove_all_channels()
  4. Create a Realtime channel

    main

    To use Realtime, instantiate an AsyncRealtimeClient with your REALTIME_URL and API_KEY, then create a channel using socket.channel("channel-name"). You can subscribe to a channel and provide a callback to handle subscription status updates using RealtimeSubscribeStates.

    import asyncio
    from typing import Optional
    from realtime import AsyncRealtimeClient, RealtimeSubscribeStates
    
    async def main():
        # Use ws://localhost:4000/websocket for local dev
        # Use wss://<project_ref>.supabase.co/realtime/v1 for Supabase projects
        REALTIME_URL = "ws://localhost:4000/websocket"
        API_KEY = "1234567890"
    
        socket = AsyncRealtimeClient(REALTIME_URL, API_KEY)
        channel = socket.channel("test-channel")
    
        def _on_subscribe(status: RealtimeSubscribeStates, err: Optional[Exception]):
            if status == RealtimeSubscribeStates.SUBSCRIBED:
                print("Connected!")
            elif status == RealtimeSubscribeStates.CHANNEL_ERROR:
                print(f"There was an error subscribing to channel: {err.args}")
            elif status == RealtimeSubscribeStates.TIMED_OUT:
                print("Realtime server did not respond in time.")
            elif status == RealtimeSubscribeStates.CLOSED:
                print("Realtime channel was unexpectedly closed.")
    
        await channel.subscribe(_on_subscribe)
  5. Listen to Postgres Change Data Capture (CDC)

    main

    Postgres CDC allows you to listen for changes (INSERT, UPDATE, DELETE, etc.) in your database tables.

    API Method:

    • channel.on_postgres_changes(event, schema, table, filter, callback)

    Parameters:

    • event: The type of change to listen for (e.g., "INSERT", "UPDATE", or "*" for all).
    • schema: The database schema (e.g., "public").
    • table: The specific table to monitor.
    • filter: A string filter for specific rows (e.g., "username=eq.Realtime").
    • callback: A function that receives the change payload.
    channel = client.channel("db-changes")
    
    # Listen to all changes in public schema
    channel.on_postgres_changes(
        "*",
        schema="public",
        callback=lambda payload: print("All changes in public schema: ", payload),
    )
    
    # Listen to specific table inserts
    channel.on_postgres_changes(
        "INSERT",
        schema="public",
        table="messages",
        callback=lambda payload: print("All inserts in messages table: ", payload),
    )
    
    # Listen to specific updates with a filter
    channel.on_postgres_changes(
        "UPDATE",
        schema="public",
        table="users",
        filter="username=eq.Realtime",
        callback=lambda payload: print(
            "All updates on users table when username is Realtime: ", payload
        ),
    )
    
    await channel.subscribe(
        lambda status, err: status == RealtimeSubscribeStates.SUBSCRIBED
        and print("Ready to receive database changes!")
    )
  6. Configure Broadcast and Presence on a Channel

    main

    When setting up a channel, you can provide configuration for broadcast and presence features using RealtimeChannelOptions and RealtimeChannelConfig types.

    • broadcast: Can include ack (boolean) and self (boolean) keys.
    • presence: Can include a key (string) to identify the presence group.
    • private: A boolean indicating if the channel is private.
    # Example configuration structure
    options = {
        "config": {
            "broadcast": {
                "ack": True,
                "self": False
            },
            "presence": {
                "key": "room_1"
            },
            "private": False
        }
    }
  7. Send messages via AsyncRealtimeClient

    main

    The send() method allows you to send messages through the WebSocket connection.

    Important: While passing a dictionary is currently supported, it is deprecated. You should use a Message object instead to ensure proper serialization and compatibility with future versions.

    If the client is not currently connected, messages sent via send() are buffered and will be flushed automatically once the connection is established.

    from realtime.message import Message
    
    # Recommended: Use a Message object
    msg = Message(topic="realtime:room_1", event="new_message", payload={"text": "hello"}, ref="1")
    await client.send(msg)
    
    # Deprecated: Passing a dictionary
    await client.send({"topic": "realtime:room_1", "event": "new_message", "payload": {}})
  8. Use SyncRealtimeClient for synchronous real-time features

    main

    For standard synchronous Python applications, use SyncRealtimeClient. It provides the same real-time capabilities (Broadcast, Presence, and Postgres CDC) but uses synchronous blocking calls.

    from realtime import SyncRealtimeClient
    
    # Example initialization
    client = SyncRealtimeClient("YOUR_SUPABASE_REALTIME_URL", "YOUR_API_KEY")
  9. Parse incoming server messages with ServerMessageAdapter

    main

    To correctly parse incoming JSON data from the Realtime server into the appropriate Pydantic model, use the ServerMessageAdapter. This adapter handles the union of all possible ServerMessage types based on the event field.

    from realtime.message import ServerMessageAdapter
    
    # Assuming 'raw_json' is the dictionary received from the websocket
    message = ServerMessageAdapter.validate_python(raw_json)
    
    if message.event == 'broadcast':
        print(f"Received broadcast on {message.topic}: {message.payload}")