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}