Crate supabase_rs

Source
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 handling
  • Insert if unique: Conditional inserts with uniqueness validation
  • Update: Modify existing rows by ID or custom columns
  • Upsert: Insert or update with conflict resolution
  • Select: Retrieve data with advanced filtering and pagination
  • Delete: 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

FeatureDescriptionStability
storageFile operations with Supabase Storageβœ… Stable
rustlsUse rustls instead of OpenSSL for TLSβœ… Stable
nightlyExperimental GraphQL support⚠️ Experimental

Β§Feature Flag Details

  • storage: Enables the [storage] module for file upload/download operations
  • rustls: Forces the HTTP client to use rustls 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 TypeUse CasePermissions
Anon KeyClient-side appsRespects RLS policies
Service RoleServer-side appsBypasses 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 operations
  • update - Update and upsert operations
  • select - Query execution and response handling
  • delete - Delete operations
  • query_builder - Fluent query building API
  • [storage] - File operations (requires storage feature)
  • graphql - GraphQL support (requires nightly 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Β§

SupabaseClient
The main client for interacting with Supabase services.

FunctionsΒ§

generate_random_id
Generates a random 64-bit signed integer within a larger range.