use crate::client::backend::{
BackendError, BackendResult, BackendType, DatabaseBackend, HealthStatus, PostgrestMethod,
QueryLanguage, QueryResult, TranslatedQuery,
};
use crate::drivers::supabase::client::{HealthAwareSupabaseClient, SupabaseConnectionInfo};
use async_trait::async_trait;
use serde_json::Value;
pub struct SupabaseBackend {
client: HealthAwareSupabaseClient,
}
impl SupabaseBackend {
pub fn new(info: SupabaseConnectionInfo) -> BackendResult<Self> {
let client: HealthAwareSupabaseClient =
HealthAwareSupabaseClient::new(info).map_err(|err| {
BackendError::Generic(format!("failed to build supabase client: {err}"))
})?;
Ok(Self { client })
}
pub fn from_env(url_key: &str, key_key: &str) -> BackendResult<Self> {
let info: SupabaseConnectionInfo = SupabaseConnectionInfo::from_env(url_key, key_key)
.map_err(|err| BackendError::Generic(err.to_string()))?;
Self::new(info)
}
}
#[async_trait]
impl DatabaseBackend for SupabaseBackend {
async fn execute_query(&self, query: TranslatedQuery) -> BackendResult<QueryResult> {
if !matches!(query.language, QueryLanguage::Postgrest) {
return Err(BackendError::Generic(
"Supabase backend only supports PostgREST-style queries".to_string(),
));
}
let method: PostgrestMethod = query.postgrest_method.unwrap_or(PostgrestMethod::Get);
if !matches!(method, PostgrestMethod::Get) {
return Err(BackendError::Generic(
"Direct Supabase backend currently supports PostgREST GET only; use gateway-routed mode for condition-based mutations".to_string(),
));
}
let table: &str = query.table.as_deref().unwrap_or("");
let rows: Vec<Value> = self
.client
.execute(table, &query.sql)
.await
.map_err(|err| BackendError::Generic(err.to_string()))?;
let columns: Vec<String> = rows
.first()
.and_then(Value::as_object)
.map(|object| object.keys().cloned().collect::<Vec<_>>())
.unwrap_or_default();
Ok(QueryResult::new(rows, columns, None, None, None))
}
async fn health_check(&self) -> BackendResult<HealthStatus> {
if let Some(deadline) = self.client.is_offline() {
Ok(HealthStatus::Offline(deadline))
} else {
Ok(HealthStatus::Healthy)
}
}
fn backend_type(&self) -> BackendType {
BackendType::Supabase
}
fn supports_sql(&self) -> bool {
false
}
fn supports_cql(&self) -> bool {
false
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}