spo_postgrest/
lib.rs

1//! # postgrest-rs
2//!
3//! [PostgREST][postgrest] client-side library.
4//!
5//! This library is a thin wrapper that brings an ORM-like interface to
6//! PostgREST.
7//!
8//! ## Usage
9//!
10//! Simple example:
11//! ```
12//! use postgrest::Postgrest;
13//!
14//! # async fn run() -> Result<(), Box<dyn std::error::Error>> {
15//! let client = Postgrest::new("https://your.postgrest.endpoint");
16//! let resp = client
17//!     .from("your_table")
18//!     .select("*")
19//!     .execute()
20//!     .await?;
21//! let body = resp
22//!     .text()
23//!     .await?;
24//! # Ok(())
25//! # }
26//! ```
27//!
28//! Using filters:
29//! ```
30//! # use postgrest::Postgrest;
31//! # async fn run() -> Result<(), Box<dyn std::error::Error>> {
32//! # let client = Postgrest::new("https://your.postgrest.endpoint");
33//! let resp = client
34//!     .from("countries")
35//!     .eq("name", "Germany")
36//!     .gte("id", "20")
37//!     .select("*")
38//!     .execute()
39//!     .await?;
40//! # Ok(())
41//! # }
42//! ```
43//!
44//! Updating a table:
45//! ```
46//! # use postgrest::Postgrest;
47//! # async fn run() -> Result<(), Box<dyn std::error::Error>> {
48//! # let client = Postgrest::new("https://your.postgrest.endpoint");
49//! let resp = client
50//!     .from("users")
51//!     .eq("username", "soedirgo")
52//!     .update("{\"organization\": \"supabase\"}")
53//!     .execute()
54//!     .await?;
55//! # Ok(())
56//! # }
57//! ```
58//!
59//! Executing stored procedures:
60//! ```
61//! # use postgrest::Postgrest;
62//! # async fn run() -> Result<(), Box<dyn std::error::Error>> {
63//! # let client = Postgrest::new("https://your.postgrest.endpoint");
64//! let resp = client
65//!     .rpc("add", r#"{"a": 1, "b": 2}"#)
66//!     .execute()
67//!     .await?;
68//! # Ok(())
69//! # }
70//! ```
71//!
72//! Check out the [README][readme] for more info.
73//!
74//! [postgrest]: https://postgrest.org
75//! [readme]: https://github.com/supabase/postgrest-rs
76
77mod builder;
78mod filter;
79
80pub use builder::{Builder, Query};
81use reqwest::header::{HeaderMap, HeaderValue, IntoHeaderName};
82use reqwest::Client;
83
84#[derive(Clone, Debug)]
85pub struct Postgrest {
86    url: String,
87    schema: Option<String>,
88    headers: HeaderMap,
89    client: Client,
90}
91
92impl Postgrest {
93    /// Creates a Postgrest client.
94    ///
95    /// # Example
96    ///
97    /// ```
98    /// use postgrest::Postgrest;
99    ///
100    /// let client = Postgrest::new("http://your.postgrest.endpoint");
101    /// ```
102    pub fn new<T>(url: T) -> Self
103    where
104        T: Into<String>,
105    {
106        Postgrest {
107            url: url.into(),
108            schema: None,
109            headers: HeaderMap::new(),
110            client: Client::new(),
111        }
112    }
113
114    /// Creates a Postgrest client.
115    ///
116    /// # Example
117    ///
118    /// ```
119    /// use postgrest::Postgrest;
120    /// use reqwest::Client;
121    ///
122    /// let client = Postgrest::new("http://your.postgrest.endpoint", Client::new());
123    /// ```
124    pub fn new_with_client<T>(url: T, client: Client) -> Self
125    where
126        T: Into<String>,
127    {
128        Postgrest {
129            url: url.into(),
130            schema: None,
131            headers: HeaderMap::new(),
132            client,
133        }
134    }
135
136
137
138    /// Switches the schema.
139    ///
140    /// # Note
141    ///
142    /// You can only switch schemas before you call `from` or `rpc`.
143    ///
144    /// # Example
145    ///
146    /// ```
147    /// use postgrest::Postgrest;
148    ///
149    /// let client = Postgrest::new("http://your.postgrest.endpoint");
150    /// client.schema("private");
151    /// ```
152    pub fn schema<T>(mut self, schema: T) -> Self
153    where
154        T: Into<String>,
155    {
156        self.schema = Some(schema.into());
157        self
158    }
159
160    /// Add arbitrary headers to the request. For instance when you may want to connect
161    /// through an API gateway that needs an API key header.
162    ///
163    /// # Example
164    ///
165    /// ```
166    /// use postgrest::Postgrest;
167    ///
168    /// let client = Postgrest::new("https://your.postgrest.endpoint")
169    ///     .insert_header("apikey", "super.secret.key")
170    ///     .from("table");
171    /// ```
172    pub fn insert_header(
173        mut self,
174        header_name: impl IntoHeaderName,
175        header_value: impl AsRef<str>,
176    ) -> Self {
177        self.headers.insert(
178            header_name,
179            HeaderValue::from_str(header_value.as_ref()).expect("Invalid header value."),
180        );
181        self
182    }
183
184    /// Perform a table operation.
185    ///
186    /// # Example
187    ///
188    /// ```
189    /// use postgrest::Postgrest;
190    ///
191    /// let client = Postgrest::new("http://your.postgrest.endpoint");
192    /// client.from("table");
193    /// ```
194    pub fn from<T>(&self, table: T) -> Builder
195    where
196        T: AsRef<str>,
197    {
198        let url = format!("{}/{}", self.url, table.as_ref());
199        Builder::new(
200            url,
201            self.schema.clone(),
202            self.headers.clone(),
203            self.client.clone(),
204        )
205    }
206
207    /// Perform a stored procedure call.
208    ///
209    /// # Example
210    ///
211    /// ```
212    /// use postgrest::Postgrest;
213    ///
214    /// let client = Postgrest::new("http://your.postgrest.endpoint");
215    /// client.rpc("multiply", r#"{"a": 1, "b": 2}"#);
216    /// ```
217    pub fn rpc<T, U>(&self, function: T, params: U) -> Builder
218    where
219        T: AsRef<str>,
220        U: Into<String>,
221    {
222        let url = format!("{}/rpc/{}", self.url, function.as_ref());
223        Builder::new(
224            url,
225            self.schema.clone(),
226            self.headers.clone(),
227            self.client.clone(),
228        )
229        .rpc(params)
230    }
231}
232
233#[cfg(test)]
234mod tests {
235    use super::*;
236
237    const REST_URL: &str = "http://localhost:3000";
238
239    #[test]
240    fn initialize() {
241        assert_eq!(Postgrest::new(REST_URL).url, REST_URL);
242    }
243
244    #[test]
245    fn switch_schema() {
246        assert_eq!(
247            Postgrest::new(REST_URL).schema("private").schema,
248            Some("private".to_string())
249        );
250    }
251
252    #[test]
253    fn with_insert_header() {
254        assert_eq!(
255            Postgrest::new(REST_URL)
256                .insert_header("apikey", "super.secret.key")
257                .headers
258                .get("apikey")
259                .unwrap(),
260            "super.secret.key"
261        );
262    }
263}