Expand description
Β§Supabase SDK for Rust
An unofficial, lightweight Rust SDK for Supabase that provides a clean, type-safe interface for interacting with Supabaseβs REST and GraphQL APIs.
This crate focuses on developer experience with a fluent, chainable API design that feels natural in Rust while maintaining compatibility with Supabaseβs PostgREST conventions.
Β§π Core Features
Β§Database Operations
Insert
: Add new rows with automatic ID generation and conflict handlingInsert if unique
: Conditional inserts with uniqueness validationUpdate
: Modify existing rows by ID or custom columnsUpsert
: Insert or update with conflict resolutionSelect
: Retrieve data with advanced filtering and paginationDelete
: Remove rows by ID or custom criteria
Β§Query Building
- Fluent API: Chain filters, sorts, and pagination naturally
- Type Safety: Leverage Rustβs type system for compile-time guarantees
- Performance: Built-in connection pooling and efficient query construction
Β§Advanced Features
Storage
: File upload/download operations (feature-gated)GraphQL
: Advanced querying with GraphQL (experimental)Realtime
: Live data subscriptions (planned)
Β§π― Feature Flags
Feature | Description | Stability |
---|---|---|
storage | File operations with Supabase Storage | β Stable |
rustls | Use rustls instead of OpenSSL for TLS | β Stable |
nightly | Experimental GraphQL support | β οΈ Experimental |
Β§Feature Flag Details
storage
: Enables the [storage
] module for file upload/download operationsrustls
: Forces the HTTP client to userustls
instead of OpenSSL (recommended for Alpine Linux)nightly
: Unlocks experimental GraphQL capabilities with detailed debugging
Β§β οΈ Nightly Features
Nightly features are experimental and may introduce breaking changes without notice. Use with caution in production environments.
To disable nightly warning messages:
SUPABASE_RS_NO_NIGHTLY_MSG=true
Β§ποΈ Architecture Overview
The SDK is built around a central SupabaseClient
that manages:
- HTTP connection pooling via
reqwest::Client
- Authentication headers and API key management
- Endpoint URL construction and routing
- Request/response serialization
Β§Module Organization
supabase_rs/
βββ lib.rs # Main client and public API
βββ insert.rs # Insert operations and bulk operations
βββ update.rs # Update and upsert operations
βββ select.rs # Query execution and response handling
βββ delete.rs # Delete operations
βββ query_builder/ # Fluent query building
β βββ builder.rs # QueryBuilder implementation
β βββ filter.rs # Filter operations (eq, gt, lt, etc.)
β βββ sort.rs # Sorting and ordering
βββ storage/ # File operations (feature-gated)
βββ graphql/ # GraphQL support (experimental)
βββ errors.rs # Error types and handling
βββ request/ # HTTP request utilities
Β§π¦ Installation
Add to your Cargo.toml
:
[dependencies]
supabase-rs = "0.4.14"
# With optional features
supabase-rs = { version = "0.4.14", features = ["storage", "rustls"] }
Β§π Quick Start
use supabase_rs::SupabaseClient;
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize client
let client = SupabaseClient::new(
std::env::var("SUPABASE_URL")?,
std::env::var("SUPABASE_KEY")?,
)?;
// Insert data
let id = client.insert("users", json!({
"name": "John Doe",
"email": "john@example.com"
})).await?;
// Query data
let users = client
.select("users")
.eq("name", "John Doe")
.limit(10)
.execute()
.await?;
println!("Found {} users", users.len());
Ok(())
}
Β§π Core Concepts
Β§Client Initialization
The SupabaseClient
is the main entry point for all operations. Itβs designed to be:
- Clone-friendly: Cheap to clone, shares connection pool
- Thread-safe: Can be used across async tasks
- Connection-pooled: Reuses HTTP connections efficiently
Β§Query Builder Pattern
The SDK uses a fluent query builder pattern for constructing complex queries:
use supabase_rs::SupabaseClient;
use serde_json::Value;
let results: Vec<Value> = client
.from("posts") // Start with table
.columns(vec!["id", "title"]) // Select specific columns
.eq("status", "published") // Add filters
.gte("created_at", "2024-01-01") // Multiple filters
.order("created_at", false) // Sort by date, newest first
.limit(20) // Limit results
.execute() // Execute query
.await?;
Β§Error Handling Philosophy
The SDK uses Result<T, String>
for most operations to provide clear error messages:
match client.insert("users", json!({"email": "test@example.com"})).await {
Ok(id) => println!("Created user with ID: {}", id),
Err(err) => {
if err.contains("409") {
println!("User already exists");
} else {
println!("Unexpected error: {}", err);
}
}
}
Β§π Authentication & Setup
The SDK requires two pieces of information to connect to your Supabase project:
- Project URL: Your unique Supabase project URL
- API Key: Either your anon key (client-side) or service role key (server-side)
Β§Environment Configuration
Set up your environment variables in a .env
file:
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_KEY=your-anon-or-service-role-key
Β§Key Types and Usage
Key Type | Use Case | Permissions |
---|---|---|
Anon Key | Client-side apps | Respects RLS policies |
Service Role | Server-side apps | Bypasses RLS, full access |
Β§π Complete Examples
Β§Client Initialization Patterns
use supabase_rs::SupabaseClient;
use dotenv::dotenv;
// Basic initialization with error handling
fn create_client() -> Result<SupabaseClient, Box<dyn std::error::Error>> {
dotenv().ok();
let client = SupabaseClient::new(
std::env::var("SUPABASE_URL")?,
std::env::var("SUPABASE_KEY")?,
)?;
Ok(client)
}
// For applications that need shared client instances
use std::sync::Arc;
fn create_shared_client() -> Arc<SupabaseClient> {
let client = SupabaseClient::new(
std::env::var("SUPABASE_URL").expect("SUPABASE_URL required"),
std::env::var("SUPABASE_KEY").expect("SUPABASE_KEY required"),
).expect("Failed to create Supabase client");
Arc::new(client)
}
Β§Insert Operations
use supabase_rs::SupabaseClient;
use serde_json::json;
// Basic insert with automatic ID generation
let user_id = client.insert("users", json!({
"name": "Alice Johnson",
"email": "alice@example.com",
"age": 28
})).await?;
println!("Created user with ID: {}", user_id);
// Insert with uniqueness check (prevents duplicates)
let unique_id = client.insert_if_unique("users", json!({
"email": "unique@example.com",
"username": "unique_user"
})).await?;
// Bulk insert for multiple records
use serde::Serialize;
#[derive(Serialize)]
struct NewUser {
name: String,
email: String,
}
let users = vec![
NewUser { name: "Bob".to_string(), email: "bob@example.com".to_string() },
NewUser { name: "Carol".to_string(), email: "carol@example.com".to_string() },
];
client.bulk_insert("users", users).await?;
Β§Update & Upsert Operations
// Update existing record by ID
client.update("users", "123", json!({
"name": "Alice Smith",
"last_login": "2024-01-15T10:30:00Z"
})).await?;
// Update by custom column
client.update_with_column_name(
"users",
"email", // Column to match
"alice@example.com", // Value to match
json!({ "verified": true })
).await?;
// Upsert (insert or update if exists)
client.upsert("settings", "user_123", json!({
"theme": "dark",
"notifications": true
})).await?;
Β§Query Operations
// Basic select with filtering
let active_users: Vec<Value> = client
.select("users")
.eq("status", "active")
.order("created_at", false) // Newest first
.limit(50)
.execute()
.await?;
// Select specific columns (more efficient)
let user_emails: Vec<Value> = client
.from("users")
.columns(vec!["id", "email", "name"])
.gte("age", "18") // Adults only
.execute()
.await?;
// Complex filtering with multiple conditions
let filtered_posts: Vec<Value> = client
.select("posts")
.eq("published", "true")
.in_("category", &["tech", "science", "programming"])
.text_search("content", "rust programming")
.limit(10)
.execute()
.await?;
// Pagination using range (recommended)
let page_1: Vec<Value> = client
.from("articles")
.range(0, 24) // First 25 items (0-24 inclusive)
.order("published_at", false)
.execute()
.await?;
Β§Delete Operations
// Delete by ID
client.delete("users", "123").await?;
// Delete by custom column
client.delete_without_defined_key("sessions", "token", "abc123").await?;
Β§Count Operations
β οΈ Performance Warning: Count operations can be expensive on large tables.
// Count all records (expensive)
let total = client
.select("users")
.count()
.execute()
.await?;
// Count with filters (more efficient)
let active_count = client
.select("users")
.eq("status", "active")
.count()
.execute()
.await?;
Β§π Module Documentation
For detailed documentation on specific functionality:
insert
- Insert operations and bulk operationsupdate
- Update and upsert operationsselect
- Query execution and response handlingdelete
- Delete operationsquery_builder
- Fluent query building API- [
storage
] - File operations (requiresstorage
feature) graphql
- GraphQL support (requiresnightly
feature)errors
- Error types and handling utilities
Β§π Whatβs Next
This SDK is actively maintained and continuously improved. Upcoming features include:
- Enhanced Realtime subscriptions
- Advanced authentication helpers
- Improved type generation utilities
- Performance optimizations
Β§π€ Contributing
Contributions are welcome! Please check our GitHub repository for contribution guidelines and open issues.
ModulesΒ§
- delete
- Delete Operations
- errors
- Error Handling and Types
- graphql
- GraphQL for Supabase
- insert
- Insert Operations
- nightly
- query
- Query Building and Execution
- query_
builder - realtime
- request
- routing
- select
- Select Operations and Query Building
- success
- Success response types.
- tests
- type_
gen - update
- Update and Upsert Operations
StructsΒ§
- Supabase
Client - The main client for interacting with Supabase services.
FunctionsΒ§
- generate_
random_ id - Generates a random 64-bit signed integer within a larger range.