Skip to main content

supabase_client_query/
lib.rs

1//! # supabase-client-query
2//!
3//! Query builder, filters, modifiers, and SQL/PostgREST execution for
4//! the `supabase-client` crate family.
5//!
6//! Provides a fluent API for SELECT, INSERT, UPDATE, DELETE, UPSERT, and RPC
7//! queries against Supabase, with 20+ filter methods and full modifier support.
8//!
9//! **Most users should depend on [`supabase-client`](https://crates.io/crates/supabase-client)
10//! instead** and enable the `query` feature (on by default), which re-exports
11//! this crate.
12//!
13//! ## Features
14//!
15//! - `direct-sql` — Execute queries directly against PostgreSQL via sqlx
16//!   instead of going through PostgREST.
17
18pub mod sql;
19pub mod table;
20pub mod filter;
21pub mod modifier;
22pub mod backend;
23pub mod postgrest;
24pub mod postgrest_execute;
25pub mod builder;
26pub mod select;
27pub mod csv_select;
28pub mod geojson_select;
29pub mod insert;
30pub mod update;
31pub mod delete;
32pub mod upsert;
33pub mod rpc;
34
35#[cfg(feature = "direct-sql")]
36pub mod generate;
37#[cfg(feature = "direct-sql")]
38pub mod execute;
39
40pub use sql::*;
41pub use table::Table;
42pub use filter::{Filterable, FilterCollector};
43pub use modifier::Modifiable;
44pub use backend::QueryBackend;
45pub use builder::{QueryBuilder, TypedQueryBuilder};
46pub use select::SelectBuilder;
47pub use insert::InsertBuilder;
48pub use update::UpdateBuilder;
49pub use delete::DeleteBuilder;
50pub use upsert::UpsertBuilder;
51pub use rpc::{RpcBuilder, TypedRpcBuilder};
52pub use csv_select::CsvSelectBuilder;
53pub use geojson_select::GeoJsonSelectBuilder;
54
55// Re-export Phase 10 types for convenience
56pub use sql::{ExplainOptions, ExplainFormat, CountOption};
57
58use std::sync::Arc;
59use serde::de::DeserializeOwned;
60use serde_json::Value as JsonValue;
61use supabase_client_core::SupabaseClient;
62
63/// Extension trait adding query builder methods to SupabaseClient.
64pub trait SupabaseClientQueryExt {
65    /// Start a dynamic (string-based) query on a table.
66    fn from(&self, table: &str) -> QueryBuilder;
67
68    /// Start a typed query on a table using the Table trait.
69    fn from_typed<T: Table>(&self) -> TypedQueryBuilder<T>;
70
71    /// Call a stored procedure/function with dynamic return.
72    fn rpc(&self, function: &str, args: JsonValue) -> Result<RpcBuilder, supabase_client_core::SupabaseError>;
73
74    /// Call a stored procedure/function with typed return.
75    fn rpc_typed<T>(&self, function: &str, args: JsonValue) -> Result<TypedRpcBuilder<T>, supabase_client_core::SupabaseError>
76    where
77        T: DeserializeOwned + Send;
78}
79
80impl SupabaseClientQueryExt for SupabaseClient {
81    fn from(&self, table: &str) -> QueryBuilder {
82        let backend = make_backend(self);
83        QueryBuilder::new(backend, self.schema().to_string(), table.to_string())
84    }
85
86    fn from_typed<T: Table>(&self) -> TypedQueryBuilder<T> {
87        let backend = make_backend(self);
88        let schema = if T::schema_name() != "public" {
89            T::schema_name().to_string()
90        } else {
91            self.schema().to_string()
92        };
93        TypedQueryBuilder::new(backend, schema)
94    }
95
96    fn rpc(&self, function: &str, args: JsonValue) -> Result<RpcBuilder, supabase_client_core::SupabaseError> {
97        let backend = make_backend(self);
98        RpcBuilder::new(backend, self.schema().to_string(), function.to_string(), args)
99    }
100
101    fn rpc_typed<T>(&self, function: &str, args: JsonValue) -> Result<TypedRpcBuilder<T>, supabase_client_core::SupabaseError>
102    where
103        T: DeserializeOwned + Send,
104    {
105        let backend = make_backend(self);
106        TypedRpcBuilder::new(backend, self.schema().to_string(), function.to_string(), args)
107    }
108}
109
110/// Create a QueryBackend from a SupabaseClient.
111///
112/// If the `direct-sql` feature is enabled and a pool is available, uses DirectSql.
113/// Otherwise, uses the REST backend (PostgREST).
114fn make_backend(client: &SupabaseClient) -> QueryBackend {
115    #[cfg(feature = "direct-sql")]
116    {
117        if let Some(pool) = client.pool_arc() {
118            return QueryBackend::DirectSql { pool };
119        }
120    }
121
122    QueryBackend::Rest {
123        http: client.http().clone(),
124        base_url: Arc::from(client.supabase_url()),
125        api_key: Arc::from(client.api_key()),
126        schema: client.schema().to_string(),
127    }
128}