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