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;
81use reqwest::header::{HeaderMap, HeaderValue, IntoHeaderName};
82use reqwest::Client;
83
84#[derive(Clone)]
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    /// Switches the schema.
115    ///
116    /// # Note
117    ///
118    /// You can only switch schemas before you call `from` or `rpc`.
119    ///
120    /// # Example
121    ///
122    /// ```
123    /// use postgrest::Postgrest;
124    ///
125    /// let client = Postgrest::new("http://your.postgrest.endpoint");
126    /// client.schema("private");
127    /// ```
128    pub fn schema<T>(mut self, schema: T) -> Self
129    where
130        T: Into<String>,
131    {
132        self.schema = Some(schema.into());
133        self
134    }
135
136    /// Add arbitrary headers to the request. For instance when you may want to connect
137    /// through an API gateway that needs an API key header.
138    ///
139    /// # Example
140    ///
141    /// ```
142    /// use postgrest::Postgrest;
143    ///
144    /// let client = Postgrest::new("https://your.postgrest.endpoint")
145    ///     .insert_header("apikey", "super.secret.key")
146    ///     .from("table");
147    /// ```
148    pub fn insert_header(
149        mut self,
150        header_name: impl IntoHeaderName,
151        header_value: impl AsRef<str>,
152    ) -> Self {
153        self.headers.insert(
154            header_name,
155            HeaderValue::from_str(header_value.as_ref()).expect("Invalid header value."),
156        );
157        self
158    }
159
160    /// Perform a table operation.
161    ///
162    /// # Example
163    ///
164    /// ```
165    /// use postgrest::Postgrest;
166    ///
167    /// let client = Postgrest::new("http://your.postgrest.endpoint");
168    /// client.from("table");
169    /// ```
170    pub fn from<T>(&self, table: T) -> Builder
171    where
172        T: AsRef<str>,
173    {
174        let url = format!("{}/{}", self.url, table.as_ref());
175        Builder::new(
176            url,
177            self.schema.clone(),
178            self.headers.clone(),
179            self.client.clone(),
180        )
181    }
182
183    /// Perform a stored procedure call.
184    ///
185    /// # Example
186    ///
187    /// ```
188    /// use postgrest::Postgrest;
189    ///
190    /// let client = Postgrest::new("http://your.postgrest.endpoint");
191    /// client.rpc("multiply", r#"{"a": 1, "b": 2}"#);
192    /// ```
193    pub fn rpc<T, U>(&self, function: T, params: U) -> Builder
194    where
195        T: AsRef<str>,
196        U: Into<String>,
197    {
198        let url = format!("{}/rpc/{}", self.url, function.as_ref());
199        Builder::new(
200            url,
201            self.schema.clone(),
202            self.headers.clone(),
203            self.client.clone(),
204        )
205        .rpc(params)
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    use super::*;
212
213    const REST_URL: &str = "http://localhost:3000";
214
215    #[test]
216    fn initialize() {
217        assert_eq!(Postgrest::new(REST_URL).url, REST_URL);
218    }
219
220    #[test]
221    fn switch_schema() {
222        assert_eq!(
223            Postgrest::new(REST_URL).schema("private").schema,
224            Some("private".to_string())
225        );
226    }
227
228    #[test]
229    fn with_insert_header() {
230        assert_eq!(
231            Postgrest::new(REST_URL)
232                .insert_header("apikey", "super.secret.key")
233                .headers
234                .get("apikey")
235                .unwrap(),
236            "super.secret.key"
237        );
238    }
239}