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}