| Crates.io | supabase-realtime-rs |
| lib.rs | supabase-realtime-rs |
| version | 0.1.2 |
| created_at | 2025-11-21 17:14:50.554109+00 |
| updated_at | 2025-12-02 15:32:00.522003+00 |
| description | Unofficial Rust client for Supabase Realtime (Phoenix Channels) |
| homepage | |
| repository | https://github.com/Scaraude/supabase-realtime-rs |
| max_upload_size | |
| id | 1943861 |
| size | 225,486 |
A Rust client for Supabase Realtime implementing the Phoenix Channels WebSocket protocol.
Note: This is an unofficial, community-maintained client. For official clients, see supabase-community.
use supabase_realtime_rs::{RealtimeClient, RealtimeClientOptions, ChannelEvent};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Connect to Supabase Realtime
let client = RealtimeClient::new(
"wss://your-project.supabase.co/realtime/v1",
RealtimeClientOptions {
api_key: "your-anon-key".to_string(),
..Default::default()
},
)?;
client.connect().await?;
// Subscribe to a channel
let channel = client.channel("room:lobby", Default::default()).await;
let mut rx = channel.on(ChannelEvent::broadcast("message")).await;
channel.subscribe().await?;
// Send and receive messages
channel.send(
ChannelEvent::broadcast("message"),
serde_json::json!({"text": "Hello!"})
).await?;
// Listen for messages
tokio::spawn(async move {
while let Some(msg) = rx.recv().await {
println!("Received: {:?}", msg);
}
});
Ok(())
}
Arc<RwLock<T>>native-tlscargo add supabase-realtime-rs
use supabase_realtime_rs::{PostgresChangesFilter, PostgresChangeEvent};
let channel = client.channel("db-changes", Default::default()).await;
// Listen for all changes to the "todos" table
let mut rx = channel.on_postgres_changes(
PostgresChangesFilter::new(PostgresChangeEvent::All, "public")
.table("todos")
).await;
channel.subscribe().await?;
tokio::spawn(async move {
while let Some(change) = rx.recv().await {
println!("Database change: {:?}", change);
}
});
Note: Requires Row Level Security (RLS) policies with SELECT permissions on the table.
use serde_json::json;
let channel = client.channel("room:lobby", Default::default()).await;
channel.subscribe().await?;
// Track your presence
channel.track(json!({
"user": "Alice",
"status": "online",
"cursor_x": 100,
"cursor_y": 200
})).await?;
// Get all present users
let users = channel.presence_list().await;
println!("Online users: {:?}", users);
// Stop tracking
channel.untrack().await?;
channel.push("custom_event", serde_json::json!({"data": "value"}))
.receive("ok", |payload| {
println!("Success: {:?}", payload);
})
.receive("error", |payload| {
eprintln!("Error: {:?}", payload);
})
.receive("timeout", |_| {
eprintln!("Request timed out");
})
.send()
.await?;
The examples/ directory contains working code for all features:
# Setup (first time only)
cp .env.example .env
# Edit .env with your Supabase credentials
# Run examples
cargo run --example test_connection # Basic WebSocket connection
cargo run --example test_channel # Channel subscriptions
cargo run --example test_send # Broadcasting messages
cargo run --example test_postgres_changes # Database event streaming
cargo run --example test_presence # Presence tracking
cargo run --example test_push # Push with acknowledgments
Check out realtime-chat-demo - a full-featured chat application built with this library, demonstrating broadcast messaging, presence tracking, and WebSocket connection management.
Production-ready features:
Known limitations:
See Cargo.toml for dependency versions.
Performance: Zero-cost abstractions, compiled binary, native async/await
Safety: Memory-safe and thread-safe by default, preventing entire classes of bugs
Use Cases:
Key differences from @supabase/realtime-js:
| Concept | JavaScript | Rust |
|---|---|---|
| Callbacks | channel.on('event', (payload) => {}) |
let mut rx = channel.on(event).await |
| Error Handling | try/catch |
Result<T, E> with ? operator |
| Async | async/await (Promise-based) |
async/await (Future-based with Tokio) |
| Shared State | Direct mutation | Arc<RwLock<T>> for thread safety |
| Event Listening | Single callback per event | mpsc channels (multiple consumers possible) |
Example comparison:
// JavaScript
const channel = client.channel("room:lobby");
channel.on("broadcast", { event: "message" }, (payload) => {
console.log(payload);
});
await channel.subscribe();
// Rust
let channel = client.channel("room:lobby", Default::default()).await;
let mut rx = channel.on(ChannelEvent::broadcast("message")).await;
channel.subscribe().await?;
tokio::spawn(async move {
while let Some(payload) = rx.recv().await {
println!("{:?}", payload);
}
});
Built on idiomatic Rust patterns:
Uses Phoenix Channels protocol for compatibility with Supabase Realtime.
# Check compilation
cargo check
# Run tests
cargo test
# Format code
cargo fmt
# Lint
cargo clippy --all-features -- -D warnings
# Generate documentation
cargo doc --open
Contributions are welcome! Here's how to get started:
git checkout -b feature/amazing-feature)cargo test && cargo clippy)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)Code style:
///) to public APIsResult<T, E> for error handling, avoid unwrap()Need help? Open an issue or discussion!
MIT License - see LICENSE for details
Made with 🦀 by the Rust community