Supabase API API in Rust

Supabase API API in Rust

This guide shows you how to integrate the Supabase API into your Rust application. Open-source Firebase alternative with PostgreSQL, Auth, Storage, and Realtime.

Prerequisites

  • Rust installed on your system
  • An Supabase API API key (sign up at https://supabase.com)
  • Basic familiarity with HTTP APIs

Installation

cargo add reqwest --features json
cargo add tokio --features full

Basic Usage

use std::env;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api_key = env::var("API_KEY").unwrap_or_else(|_| "YOUR_API_KEY".to_string());
    let base_url = "https://api.supabase.com";
    let url = format!("{}/v1/resources", base_url);

    let client = reqwest::Client::new();
    let resp = client
        .get(&url)
        .bearer_auth(&api_key)
        .header("Content-Type", "application/json")
        .send()
        .await?;

    println!("Status: {}", resp.status());
    let body = resp.text().await?;
    println!("{}", body);
    Ok(())
}

Error Handling

use std::time::Duration;
use std::env;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api_key = env::var("API_KEY").unwrap_or_else(|_| "YOUR_API_KEY".to_string());
    let base_url = "https://api.supabase.com";

    let client = reqwest::Client::builder()
        .timeout(Duration::from_secs(30))
        .build()?;

    let mut attempt = 0;
    let max_retries = 3;
    loop {
        let resp = client
            .get(format!("{}/v1/resources", base_url))
            .bearer_auth(&api_key)
            .send()
            .await?;

        if resp.status().as_u16() == 429 {
            println!("Rate limited. Waiting 60s...");
            tokio::time::sleep(Duration::from_secs(60)).await;
            continue;
        }

        if resp.status().is_success() {
            let body = resp.text().await?;
            println!("{}", body);
            return Ok(());
        }

        attempt += 1;
        if attempt >= max_retries {
            return Err(format!("HTTP {}", resp.status()).into());
        }
        tokio::time::sleep(Duration::from_secs(2u64.pow(attempt))).await;
    }
}

Advanced Usage

use std::env;
use reqwest::{Client, Method};
use std::error::Error;

pub struct SupabaseClient {
    client: Client,
    api_key: String,
    base_url: String,
}

impl SupabaseClient {
    pub fn new(api_key: String, base_url: String) -> Self {
        Self {
            client: Client::new(),
            api_key,
            base_url: base_url.trim_end_matches('/').to_string(),
        }
    }

    pub async fn request(&self, method: Method, path: &str) -> Result<String, Box<dyn Error>> {
        let url = format!("{}/v1{}", self.base_url, path);
        let resp = self.client
            .request(method, &url)
            .bearer_auth(&self.api_key)
            .header("Content-Type", "application/json")
            .send()
            .await?;
        if !resp.status().is_success() {
            return Err(format!("API Error: HTTP {}", resp.status()).into());
        }
        Ok(resp.text().await?)
    }

    pub async fn list_resources(&self) -> Result<String, Box<dyn Error>> {
        self.request(Method::GET, "/resources").await
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let client = SupabaseClient::new(
        env::var("API_KEY")?,
        "https://api.supabase.com".to_string(),
    );
    let resources = client.list_resources().await?;
    println!("{}", resources);
    Ok(())
}

Best Practices

  • Store your API key in an environment variable, never in source code
  • Implement retry logic with exponential backoff for 429 and 5xx errors
  • Set reasonable timeouts (10-30 seconds) for all API calls
  • Log request IDs from response headers for debugging
  • Cache responses where appropriate to reduce API calls

Other languages