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}