Crate supabase_rs

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.

Re-exportsΒ§

pub use success::SupabaseErrorResponse;

ModulesΒ§

delete
Delete Operations
errors
Error Handling and Types
graphql
GraphQL Support for Supabase
insert
Insert Operations
nightly
query
Query Building and Execution
query_builder
realtime
request
routing
select
Select Operations and Query Building
success
Response handling for Supabase API calls.
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.