Getting Started with Realtime
Learn how to build real-time applications with Supabase Realtime
Quick start
1. Install the client library
npm install @supabase/supabase-jsflutter pub add supabase_flutterlet package = Package( // ... dependencies: [ // ... .package( url: "https://github.com/supabase/supabase-swift.git", from: "2.0.0" ), ], targets: [ .target( name: "YourTargetName", dependencies: [ .product( name: "Supabase", package: "supabase-swift" ), ] ) ])pip install supabaseconda install -c conda-forge supabasedotnet add package Supabase2. Initialize the client
Get your project URL and key.
Get API details
To interact with data in database tables, you use the client libraries that wrap the auto-generated Data API endpoints, authenticating using the Project URL and key from the project Connect dialog.
See API keys for a full explanation of all key types, their uses, and where to find them.
import { } from '@supabase/supabase-js'const = ('https://<project>.supabase.co', '<sb_publishable_key>')import 'package:supabase_flutter/supabase_flutter.dart';void main() async { await Supabase.initialize( url: 'https://<project>.supabase.co', publishableKey: '<sb_publishable_key>', ); runApp(MyApp());}final supabase = Supabase.instance.client;import Supabaselet supabase = SupabaseClient( supabaseURL: URL(string: "https://<project>.supabase.co")!, supabaseKey: "<sb_publishable_key>")from supabase import create_client, Clienturl: str = "https://<project>.supabase.co"key: str = "<sb_publishable_key>"supabase: Client = create_client(url, key)using Supabase;var supabase = new Client( "https://<project>.supabase.co", "<sb_publishable_key>");await supabase.InitializeAsync();3. Create your first Channel
Channels are the foundation of Realtime. Think of them as rooms where clients can communicate. Each channel is identified by a topic name and if they are public or private.
// Create a channel with a descriptive topic nameconst channel = supabase.channel('room:lobby:messages', { config: { private: true }, // Recommended for production})// Create a channel with a descriptive topic namefinal channel = supabase.channel('room:lobby:messages');// Create a channel with a descriptive topic namelet channel = supabase.channel("room:lobby:messages") { $0.isPrivate = true}# Create a channel with a descriptive topic namechannel = supabase.channel('room:lobby:messages', params={'config': {'private': True }})// Create a channel with a descriptive topic name.// Note: The C# SDK does not yet support private channel configuration,// so this connects to a public channel.var channel = supabase.Realtime.Channel("room:lobby:messages");4. Set up authorization
Since we're using a private channel, you need to create a basic RLS policy on the realtime.messages table to allow authenticated users to connect. Row Level Security (RLS) policies control who can access your Realtime channels based on user authentication and custom rules:
-- Allow authenticated users to receive broadcastsCREATE POLICY "authenticated_users_can_receive" ON realtime.messages FOR SELECT TO authenticated USING (true);-- Allow authenticated users to send broadcastsCREATE POLICY "authenticated_users_can_send" ON realtime.messages FOR INSERT TO authenticated WITH CHECK (true);5. Send and receive messages
There are three main ways to send messages with Realtime:
5.1 using client libraries
Send and receive messages using the Supabase client:
// Listen for messageschannel .on('broadcast', { event: 'message_sent' }, (payload: { payload: any }) => { console.log('New message:', payload.payload) }) .subscribe()// Send a messagechannel.send({ type: 'broadcast', event: 'message_sent', payload: { text: 'Hello, world!', user: 'john_doe', timestamp: new Date().toISOString(), },})// Listen for messageschannel.onBroadcast( event: 'message_sent', callback: (payload) { print('New message: ${payload['payload']}'); },).subscribe();// Send a messagechannel.sendBroadcastMessage( event: 'message_sent', payload: { 'text': 'Hello, world!', 'user': 'john_doe', 'timestamp': DateTime.now().toIso8601String(), },);// Listen for messagesawait channel.onBroadcast(event: "message_sent") { message in print("New message: \(message.payload)")}let status = await channel.subscribe()// Send a messageawait channel.sendBroadcastMessage( event: "message_sent", payload: [ "text": "Hello, world!", "user": "john_doe", "timestamp": ISO8601DateFormatter().string(from: Date()) ])# Listen for messagesdef message_handler(payload): print(f"New message: {payload['payload']}")channel.on_broadcast(event="message_sent", callback=message_handler).subscribe()# Send a messagechannel.send_broadcast_message( event="message_sent", payload={ "text": "Hello, world!", "user": "john_doe", "timestamp": datetime.now().isoformat() })class MessageBroadcast : BaseBroadcast{ [JsonProperty("text")] public string Text { get; set; } [JsonProperty("user")] public string User { get; set; } [JsonProperty("timestamp")] public string Timestamp { get; set; }}// Listen for messagesvar broadcast = channel.Register<MessageBroadcast>();broadcast.AddBroadcastEventHandler((sender, _) =>{ Console.WriteLine($"New message: {broadcast.Current()}");});await channel.Subscribe();// Send a messageawait broadcast.Send("message_sent", new MessageBroadcast{ Text = "Hello, world!", User = "john_doe", Timestamp = DateTime.UtcNow.ToString("o")});5.2 using HTTP/REST API
Send messages via HTTP requests, perfect for server-side applications:
// Send message via REST APIconst response = await fetch(`https://<project>.supabase.co/rest/v1/rpc/broadcast`, { method: 'POST', headers: { 'Content-Type': 'application/json', -H "apikey: <SECRET_KEY>" }, body: JSON.stringify({ topic: 'room:lobby:messages', event: 'message_sent', payload: { text: 'Hello from server!', user: 'system', timestamp: new Date().toISOString(), }, private: true, }),})import 'package:http/http.dart' as http;import 'dart:convert';// Send message via REST APIfinal response = await http.post( Uri.parse('https://<project>.supabase.co/rest/v1/rpc/broadcast'), headers: { 'Content-Type': 'application/json', -H "apikey: <SECRET_KEY>" }, body: jsonEncode({ 'topic': 'room:lobby:messages', 'event': 'message_sent', 'payload': { 'text': 'Hello from server!', 'user': 'system', 'timestamp': DateTime.now().toIso8601String(), }, 'private': true, }),);import Foundation// Send message via REST APIlet url = URL(string: "https://<project>.supabase.co/rest/v1/rpc/broadcast")!var request = URLRequest(url: url)request.httpMethod = "POST"request.setValue("application/json", forHTTPHeaderField: "Content-Type")request.setValue("<SECRET_KEY>", forHTTPHeaderField: "apikey")let payload = [ "topic": "room:lobby:messages", "event": "message_sent", "payload": [ "text": "Hello from server!", "user": "system", "timestamp": ISO8601DateFormatter().string(from: Date()) ], "private": true] as [String: Any]request.httpBody = try JSONSerialization.data(withJSONObject: payload)let (data, response) = try await URLSession.shared.data(for: request)import requestsfrom datetime import datetime# Send message via REST APIresponse = requests.post( 'https://<project>.supabase.co/rest/v1/rpc/broadcast', headers={ 'Content-Type': 'application/json', 'apikey': '<SECRET_KEY>' }, json={ 'topic': 'room:lobby:messages', 'event': 'message_sent', 'payload': { 'text': 'Hello from server!', 'user': 'system', 'timestamp': datetime.now().isoformat() }, 'private': True })5.3 using database triggers
Automatically broadcast database changes using triggers. Choose the approach that best fits your needs:
Using realtime.broadcast_changes (Best for mirroring database changes)
-- Create a trigger function for broadcasting database changesCREATE OR REPLACE FUNCTION broadcast_message_changes()RETURNS TRIGGER AS $$BEGIN -- Broadcast to room-specific channel PERFORM realtime.broadcast_changes( 'room:' || NEW.room_id::text || ':messages', TG_OP, TG_OP, TG_TABLE_NAME, TG_TABLE_SCHEMA, NEW, OLD ); RETURN NULL;END;$$ LANGUAGE plpgsql SECURITY DEFINER;-- Apply trigger to your messages tableCREATE TRIGGER messages_broadcast_trigger AFTER INSERT OR UPDATE OR DELETE ON messages FOR EACH ROW EXECUTE FUNCTION broadcast_message_changes();Using realtime.send (Best for custom notifications and filtered data)
-- Create a trigger function for custom notificationsCREATE OR REPLACE FUNCTION notify_message_activity()RETURNS TRIGGER AS $$BEGIN -- Send custom notification when new message is created IF TG_OP = 'INSERT' THEN PERFORM realtime.send( jsonb_build_object( 'message_id', NEW.id, 'user_id', NEW.user_id, 'room_id', NEW.room_id, 'created_at', NEW.created_at ), 'message_created', 'room:' || NEW.room_id::text || ':notifications', true -- private channel ); END IF; RETURN NULL;END;$$ LANGUAGE plpgsql SECURITY DEFINER;-- Apply trigger to your messages tableCREATE TRIGGER messages_notification_trigger AFTER INSERT ON messages FOR EACH ROW EXECUTE FUNCTION notify_message_activity();realtime.broadcast_changessends the full database change with metadatarealtime.sendallows you to send custom payloads and control exactly what data is broadcast
Essential best practices
Use private channels
Always use private channels for production applications to ensure proper security and authorization:
const channel = supabase.channel('room:123:messages', { config: { private: true },})Follow naming conventions
Channel Topics: Use the pattern scope:id:entity
room:123:messages- Messages in room 123game:456:moves- Game moves for game 456user:789:notifications- Notifications for user 789
Clean up subscriptions
Always unsubscribe when you are done with a channel to ensure you free up resources:
// React exampleimport { useEffect } from 'react'useEffect(() => { const channel = supabase.channel('room:123:messages') return () => { supabase.removeChannel(channel) }}, [])// Flutter exampleclass _MyWidgetState extends State<MyWidget> { RealtimeChannel? _channel; @override void initState() { super.initState(); _channel = supabase.channel('room:123:messages'); } @override void dispose() { _channel?.unsubscribe(); super.dispose(); }}// SwiftUI examplestruct ContentView: View { @State private var channel: RealtimeChannelV2? var body: some View { // Your UI here .onAppear { channel = supabase.realtimeV2.channel("room:123:messages") } .onDisappear { Task { await channel?.unsubscribe() } } }}# Python example with context managerclass RealtimeManager: def __init__(self): self.channel = None def __enter__(self): self.channel = supabase.channel('room:123:messages') return self.channel def __exit__(self, exc_type, exc_val, exc_tb): if self.channel: self.channel.unsubscribe()# Usagewith RealtimeManager() as channel: # Use channel here pass// Remove the channel when you are done with it to free up resourcessupabase.Realtime.Remove(channel);Choose the right feature
When to use Broadcast
- Real-time messaging and notifications
- Custom events and game state
- Database change notifications (with triggers)
- High-frequency updates (e.g. Cursor tracking)
- Most use cases
When to use Presence
- User online/offline status
- Active user counters
- Use minimally due to computational overhead
When to use Postgres Changes
- Quick testing and development
- Low amount of connected users
Next steps
Now that you understand the basics, dive deeper into each feature:
Core features
- Broadcast - Learn about sending messages, database triggers, and REST API usage
- Presence - Implement user state tracking and online indicators
- Postgres Changes - Understanding database change listeners (consider migrating to Broadcast)
Security & configuration
- Authorization - Set up RLS policies for private channels
- Settings - Configure your Realtime instance for optimal performance
Advanced topics
- Architecture - Understand how Realtime works under the hood
- Benchmarks - Performance characteristics and scaling considerations
- Limits - Usage limits and best practices
Integration guides
- Realtime with Next.js - Build real-time Next.js applications
- User Presence - Implement user presence features
- Database Changes - Listen to database changes
UI components
- Realtime components - Install live cursors, avatar stacks, and collaborative editors from the Supabase Library
- Messaging components - Install a realtime chat from the Supabase Library
Framework examples
- Flutter Integration - Build real-time Flutter applications
Ready to build something amazing? Start with the Broadcast guide to create your first real-time feature!