├── .gitignore ├── Cargo.toml ├── LICENSE.md ├── README.md ├── examples ├── leptos-oauth │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── index.html │ ├── src │ │ └── main.rs │ └── style.css ├── leptos-otp │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── index.html │ └── src │ │ └── main.rs ├── sycamore-auth │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── app.css │ ├── index.html │ └── src │ │ └── main.rs ├── sycamore-guestbook │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── index.html │ └── src │ │ └── main.rs ├── sycamore-realtime-chat │ ├── .gitignore │ ├── Cargo.toml │ ├── index.html │ └── src │ │ └── main.rs └── yew-supabase-auth │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── index.html │ └── src │ └── main.rs └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "supabase-js-rs" 3 | description = "Rust bindings for Supabase JavaScript library via WebAssembly" 4 | readme = "README.md" 5 | repository = "https://github.com/wa1aric/supabase-js-rs/" 6 | version = "0.1.3" 7 | edition = "2021" 8 | authors = ["Valery Stepanov "] 9 | keywords = ["supabase", "supabase-js", "wasm"] 10 | categories = ["wasm", "web-programming", "authentication", "database"] 11 | license = "MIT OR Apache-2.0" 12 | exclude = ["examples"] 13 | 14 | [dependencies] 15 | wasm-bindgen = "0.2.83" 16 | wasm-bindgen-futures = "0.4.33" 17 | js-sys = "0.3.60" 18 | 19 | [badges] 20 | maintenance = { status = "actively-developed" } 21 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Valery Stepanov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # supabase-js-rs 2 | 3 | Rust bindings for [Supabase](https://supabase.com/) JavaScript library via WebAssembly. 4 | 5 | ![Supabase Wasm](https://repository-images.githubusercontent.com/579711492/4e814ba5-3ea3-4678-906b-6595f7972928) 6 | 7 | ## Usage 8 | 9 | Add `supabase-js-rs` to Cargo.toml 10 | 11 | ``` 12 | supabase-js-rs = { version = "0.1.2" } 13 | wasm-bindgen = "0.2.83" 14 | ``` 15 | or using a git dependency 16 | ``` 17 | supabase-js-rs = { git = "https://github.com/wa1aric/supabase-js-rs", rev = "ada414750f6e5baa2f4729304c53aed3b2d9515e" } 18 | wasm-bindgen = "0.2.83" 19 | ``` 20 | 21 | Install `@supabase/supabase-js` as package by adding CDN link to index.html in the root of your crate 22 | 23 | ``` 24 | 25 | ``` 26 | 27 | Build and run 28 | 29 | ``` 30 | trunk serve 31 | ``` 32 | 33 | ## Examples 34 | 35 | - [x] [Sycamore Auth](https://github.com/wa1aric/supabase-js-rs/tree/master/examples/sycamore-auth) 36 | - [x] [Sycamore Guestbook](https://github.com/wa1aric/supabase-js-rs/tree/master/examples/sycamore-guestbook) 37 | - [x] [Sycamore Realtime](https://github.com/wa1aric/supabase-js-rs/tree/master/examples/sycamore-realtime-chat) 38 | - [ ] Perseus 39 | - [x] [Yew Auth](https://github.com/wa1aric/supabase-js-rs/tree/master/examples/yew-supabase-auth) 40 | - [ ] Seed 41 | - [x] [Leptos OAuth](https://github.com/wa1aric/supabase-js-rs/tree/master/examples/leptos-oauth) 42 | - [ ] MoonZoon 43 | 44 | ## What I've done so far 45 | 46 | - [ ] Auth 47 | - [x] Create a new user 48 | - [x] Sign in a user 49 | - [x] Sign in a user through OTP 50 | - [x] Sign in a user through OAuth 51 | - [x] Sign out a user 52 | - [ ] Verify and log in through OTP 53 | - [x] Retrieve a session 54 | - [x] Retrieve a new session 55 | - [x] Retrieve a user 56 | - [x] Update a user 57 | - [X] Set the session data 58 | - [x] Listen to auth events 59 | - [x] Send a password reset request 60 | - [ ] Enroll a factor 61 | - [ ] Create a challenge 62 | - [ ] Verify a challenge 63 | - [ ] Create and verify a challenge 64 | - [ ] Unenroll a factor 65 | - [ ] Get Authenticator Assurance Level 66 | - [x] Anonymous sign in 67 | - [ ] Database 68 | - [x] Fetch data 69 | - [x] Insert data 70 | - [x] Update data 71 | - [x] Upsert data 72 | - [x] Delete data 73 | - [ ] Call a Postgres function 74 | - [x] Using filters 75 | - [x] Using Modifiers 76 | - [ ] Functions 77 | - [x] Realtime 78 | - [ ] Storage 79 | -------------------------------------------------------------------------------- /examples/leptos-oauth/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | dist 3 | Cargo.lock -------------------------------------------------------------------------------- /examples/leptos-oauth/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "leptos-oauth" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | supabase-js-rs = { path = "../../" } 8 | leptos = { version = "0.1.0-alpha", features = ["stable"] } 9 | serde = { version = "1.0", features = ["derive"] } 10 | serde-wasm-bindgen = "0.4" 11 | wasm-bindgen = "0.2.83" 12 | web-sys = "0.3.60" -------------------------------------------------------------------------------- /examples/leptos-oauth/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wa1aric/supabase-js-rs/85124992a2f6e1d50b633cdf614ce0eebdff0162/examples/leptos-oauth/README.md -------------------------------------------------------------------------------- /examples/leptos-oauth/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Leptos OAuth with Supabase 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/leptos-oauth/src/main.rs: -------------------------------------------------------------------------------- 1 | use leptos::*; 2 | use serde::{Deserialize, Serialize}; 3 | use supabase_js_rs::{create_client, SignInWithOAuthCredentials}; 4 | use wasm_bindgen::{prelude::Closure, JsValue}; 5 | use web_sys::console::log_1; 6 | 7 | #[derive(Serialize, Deserialize)] 8 | #[serde(rename_all = "camelCase")] 9 | pub struct Options { 10 | pub redirect_to: String, 11 | } 12 | 13 | pub fn main() { 14 | mount_to_body(|cx| { 15 | let (session, set_session) = create_signal(cx, JsValue::NULL); 16 | let (client, _) = create_signal( 17 | cx, 18 | create_client(std::env!("SUPABASE_URL"), std::env!("SUPABASE_KEY")), 19 | ); 20 | 21 | let callback = Closure::new(move |event: JsValue, session: JsValue| { 22 | log_1(&event); 23 | log_1(&session); 24 | set_session.set(session); 25 | }); 26 | client.get().auth().on_auth_state_change(&callback); 27 | callback.forget(); 28 | 29 | let sign_in = move |_| { 30 | spawn_local(async move { 31 | let _result = client 32 | .get() 33 | .auth() 34 | .sign_in_with_oauth(SignInWithOAuthCredentials { 35 | provider: "github".to_string(), 36 | options: serde_wasm_bindgen::to_value(&Options { 37 | redirect_to: "http://127.0.0.1:8080/".to_string(), 38 | }) 39 | .unwrap(), 40 | }) 41 | .await; 42 | match _result { 43 | Ok(data) => log_1(&data), 44 | Err(error) => log_1(&error), 45 | } 46 | }); 47 | }; 48 | 49 | let sign_out = move |_| { 50 | let _ = spawn_local(async move { 51 | client.get().auth().sign_out().await; 52 | }); 53 | }; 54 | 55 | view! { 56 | cx, 57 |
58 | "Leptos OAuth with Supabase" 59 |

"" {move || format!("{:#?}", session.get())} ""

60 | 61 | 62 |
63 | } 64 | }) 65 | } 66 | -------------------------------------------------------------------------------- /examples/leptos-oauth/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | font-family: 'Courier New', Courier, monospace; 5 | } 6 | 7 | body { 8 | width: 100%; 9 | } 10 | 11 | .container { 12 | width: 100%; 13 | height: 100vh; 14 | display: flex; 15 | flex-direction: column; 16 | gap: 1.5em; 17 | align-items: center; 18 | justify-content: center; 19 | } 20 | 21 | button { 22 | padding: 1em 2em; 23 | } -------------------------------------------------------------------------------- /examples/leptos-otp/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | dist -------------------------------------------------------------------------------- /examples/leptos-otp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "leptos-otp" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | supabase-js-rs = { path = "../../" } 8 | leptos = { version = "0.1.0-alpha", features = ["stable"] } 9 | serde = { version = "1.0", features = ["derive"] } 10 | serde-wasm-bindgen = "0.4" -------------------------------------------------------------------------------- /examples/leptos-otp/README.md: -------------------------------------------------------------------------------- 1 | # Leptos OTP Auth with Supabase 2 | 3 | ``` 4 | $Env:SUPABASE_URL=""; $Env:SUPABASE_KEY=""; trunk serve --open 5 | ``` 6 | -------------------------------------------------------------------------------- /examples/leptos-otp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Leptos OTP Auth with Supabase 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/leptos-otp/src/main.rs: -------------------------------------------------------------------------------- 1 | use leptos::{ 2 | wasm_bindgen::{prelude::Closure, JsValue}, 3 | web_sys::console::log_1, 4 | *, 5 | }; 6 | use serde::{Deserialize, Serialize}; 7 | use supabase_js_rs::create_client; 8 | 9 | #[derive(Serialize, Deserialize)] 10 | struct SignInWithPasswordlessCredentials<'a> { 11 | pub email: Option<&'a str>, 12 | pub phone: Option<&'a str>, 13 | pub options: Option>, 14 | } 15 | 16 | #[derive(Serialize, Deserialize)] 17 | #[serde(rename_all = "camelCase")] 18 | struct Options<'a> { 19 | pub email_redirect_to: &'a str, 20 | } 21 | 22 | #[component] 23 | fn App(cx: Scope) -> impl IntoView { 24 | let (client, _) = create_signal( 25 | cx, 26 | create_client(std::env!("SUPABASE_URL"), std::env!("SUPABASE_KEY")), 27 | ); 28 | let (session, set_session) = create_signal(cx, wasm_bindgen::JsValue::NULL); 29 | 30 | let auth_callback = Closure::new(move |event: JsValue, session: JsValue| { 31 | log_1(&event); 32 | }); 33 | client.get().auth().on_auth_state_change(&auth_callback); 34 | auth_callback.forget(); 35 | 36 | let (email, set_email) = create_signal(cx, String::from("")); 37 | 38 | let sign_in = move |_| { 39 | spawn_local(async move { 40 | let email = email.get(); 41 | let credentials = SignInWithPasswordlessCredentials { 42 | email: Some(email.as_str()), 43 | phone: None, 44 | options: Some(Options { 45 | email_redirect_to: "http://127.0.0.1:8080", 46 | }), 47 | }; 48 | let res = client 49 | .get() 50 | .auth() 51 | .sign_in_with_otp(serde_wasm_bindgen::to_value(&credentials).unwrap()) 52 | .await; 53 | log_1(&res.unwrap()); 54 | }); 55 | }; 56 | view! { 57 | cx, 58 | 62 | 63 | } 64 | } 65 | 66 | fn main() { 67 | mount_to_body(|cx| { 68 | view! { 69 | cx, 70 | 71 | } 72 | }); 73 | } 74 | -------------------------------------------------------------------------------- /examples/sycamore-auth/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | dist -------------------------------------------------------------------------------- /examples/sycamore-auth/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sycamore-auth" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | supabase-js-rs = { path = "../../" } 8 | sycamore = { version = "0.8.2", features = ["suspense"] } 9 | wasm-bindgen = "0.2.83" 10 | wasm-bindgen-futures = "0.4.33" 11 | web-sys = "0.3.60" 12 | js-sys = "0.3.60" -------------------------------------------------------------------------------- /examples/sycamore-auth/README.md: -------------------------------------------------------------------------------- 1 | # Sycamore With Supabase Auth Example 2 | 3 | [Sycamore](https://sycamore-rs.netlify.app/) is an reactive library for creating web apps in Rust and WebAssembly. [Supabase](https://sycamore-supabase-js-rs-auth-demo.netlify.app/) is an open source Firebase alternative. 4 | 5 | Live demo [https://sycamore-supabase-auth.netlify.app](https://sycamore-supabase-auth.netlify.app) 6 | 7 | ## Running 8 | 9 | Add enironment variables 10 | 11 | ``` 12 | SUPABASE_URL={ACCESS_TOKEN} 13 | SUPABASE_KEY={SUPABASE_KEY} 14 | ``` 15 | 16 | of if you on PowerShell 17 | 18 | ``` 19 | $Env:SUPABASE_URL="{ACCESS_TOKEN}" 20 | $Env:SUPABASE_KEY="{SUPABASE_KEY}" 21 | ``` 22 | 23 | and then 24 | 25 | ``` 26 | trunk serve 27 | ``` 28 | -------------------------------------------------------------------------------- /examples/sycamore-auth/app.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 5 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 13 | monospace; 14 | color: white; 15 | word-break: break-all; 16 | margin: 3em; 17 | } 18 | 19 | .app { 20 | height: 100vh; 21 | background-color: #1a202c; 22 | overflow: auto; 23 | text-align: center; 24 | display: flex; 25 | flex-direction: column; 26 | justify-content: center; 27 | } 28 | 29 | .form { 30 | display: flex; 31 | flex-direction: column; 32 | justify-content: center; 33 | padding: 3em; 34 | gap: 1em; 35 | color: white; 36 | } 37 | 38 | h1 { 39 | margin: 0; 40 | font-size: 50px; 41 | font-weight: bold; 42 | color: white; 43 | } 44 | 45 | h2 { 46 | font-size: 25px; 47 | color: white; 48 | } 49 | 50 | input { 51 | display: inline-block; 52 | color: white; 53 | padding: 10px; 54 | width: 100%; 55 | height: 60px; 56 | font-size: 16px; 57 | box-sizing: border-box; 58 | background-color: rgba(0, 0, 0, 0.20); 59 | border: none; 60 | border-radius: 11px; 61 | outline: none; 62 | } 63 | 64 | button { 65 | height: 45px; 66 | border: 0; 67 | width: auto; 68 | padding-left: 40px; 69 | padding-right: 40px; 70 | border-radius: 10px; 71 | cursor: pointer; 72 | font-size: 16px; 73 | font-weight: bold; 74 | color: white; 75 | background-color: #3fcf8e; 76 | } 77 | 78 | button:disabled { 79 | background-color: rgba(0, 0, 0, 0.50); 80 | } 81 | 82 | .supabase { 83 | color: #3fcf8e; 84 | } 85 | 86 | .sycamore { 87 | color: rgb(248 113 113); 88 | } 89 | 90 | .error { 91 | color: rgb(248 113 113); 92 | } -------------------------------------------------------------------------------- /examples/sycamore-auth/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Sycamore with Supabase Auth 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/sycamore-auth/src/main.rs: -------------------------------------------------------------------------------- 1 | use js_sys::{JsString, Object, JSON}; 2 | use supabase_js_rs::*; 3 | use sycamore::{futures::spawn_local_scoped, prelude::*}; 4 | use wasm_bindgen::{prelude::Closure, JsValue}; 5 | use web_sys::console::log_1; 6 | 7 | async fn sign_in(auth: Auth, credentials: Credentials) -> Result { 8 | let res = auth.sign_in_with_password(credentials).await; 9 | match &res { 10 | Ok(data) => log_1(&data), 11 | Err(err) => log_1(&err), 12 | }; 13 | res 14 | } 15 | 16 | #[component] 17 | async fn Auth(cx: Scope<'_>) -> View { 18 | let state = use_context::>(cx); 19 | let client = use_context::>(cx); 20 | 21 | let email = create_signal(cx, String::new()); 22 | let password = create_signal(cx, String::new()); 23 | 24 | view! { 25 | cx, 26 | div(class="form") { 27 | 28 | h2 {"Signin or create account"} 29 | 30 | input(type="email", placeholder="Email", bind:value=email) {} 31 | input(type="password", placeholder="Password", bind:value=password) {} 32 | 33 | button(class="sign-in-button", disabled=email.get().is_empty() || password.get().is_empty(), on:click=move |_| { 34 | 35 | let email = email.get().to_string(); 36 | let password = password.get().to_string(); 37 | 38 | spawn_local_scoped(cx, async move { 39 | let auth = client.get().auth(); 40 | let cred = Credentials { 41 | email, 42 | password 43 | }; 44 | sign_in(auth, cred).await; 45 | }); 46 | }) { "Sign In" } 47 | 48 | button(disabled=email.get().is_empty() || password.get().is_empty(), on:click=move |_| { 49 | let email = email.get().to_string(); 50 | let password = password.get().to_string(); 51 | 52 | spawn_local_scoped(cx, async move { 53 | let res: Result = client.get().auth().sign_in_with_password(Credentials { 54 | email, 55 | password 56 | }).await; 57 | }); 58 | }) { "Create account" } 59 | 60 | // p(class="error") {"Auth error:" (format!(" {:#?}", state.get().error.get()))} 61 | } 62 | } 63 | } 64 | 65 | #[derive(Clone, Copy, PartialEq, Eq)] 66 | struct Loading(bool); 67 | 68 | impl Loading { 69 | fn is_loading(self) -> bool { 70 | self.0 71 | } 72 | } 73 | 74 | pub struct Session { 75 | pub access_key: RcSignal, 76 | pub data: RcSignal, 77 | pub error: RcSignal, 78 | } 79 | 80 | impl Session { 81 | pub fn set_access_key(&self, access_key: &str) { 82 | self.access_key.set(access_key.to_string()); 83 | } 84 | pub fn get_access_key(&self) -> String { 85 | self.access_key.get().to_string() 86 | } 87 | pub fn set_data(&self, data: JsValue) { 88 | self.data.set(data); 89 | } 90 | pub fn stringify_data(&self) -> JsString { 91 | JSON::stringify(&self.data.get()).unwrap() 92 | } 93 | } 94 | 95 | #[component] 96 | fn App(cx: Scope) -> View { 97 | let client = supabase_js_rs::create_client( 98 | &std::env!("SUPABASE_URL").to_string(), 99 | &std::env!("SUPABASE_KEY").to_string(), 100 | ); 101 | 102 | let loading = create_rc_signal(Loading(false)); 103 | provide_context(cx, loading); 104 | let use_loading_context = use_context::>(cx); 105 | let loading = use_loading_context.clone(); 106 | 107 | let create_session = create_rc_signal(Session { 108 | access_key: create_rc_signal(String::from("")), 109 | data: create_rc_signal(JsValue::NULL), 110 | error: create_rc_signal(JsValue::NULL), 111 | }); 112 | provide_context(cx, create_session); 113 | let use_create_session_context = use_context::>(cx); 114 | let session_clone = use_create_session_context.clone(); 115 | 116 | let auth_callback: Closure = 117 | Closure::new(move |_: JsValue, session: JsValue| { 118 | session_clone.get().set_data(session); 119 | loading.set(Loading(false)); 120 | }); 121 | client.auth().on_auth_state_change(&auth_callback); 122 | auth_callback.forget(); 123 | 124 | let client = create_rc_signal(client); 125 | provide_context(cx, client); 126 | 127 | view! { 128 | cx, 129 | div(class="app") { 130 | h1 { span(class="sycamore") {"Sycamore"} " with " span(class="supabase") {"Supabase"} " Auth" } 131 | ( 132 | if use_context::>(cx).get().is_loading() { 133 | view! { 134 | cx, 135 | h2 {"Loading..."} 136 | } 137 | } 138 | else { 139 | let session_context = use_context::>(cx); 140 | if Object::is(&session_context.get().stringify_data(), &JSON::stringify(&JsValue::NULL).unwrap()) { 141 | Auth(cx) 142 | } 143 | else { 144 | view! { 145 | cx, 146 | code {(format!(" {}", session_context.get().stringify_data()))} 147 | button(on:click=move |_| { 148 | let loading = use_context::>(cx); 149 | let loading_clone = loading.clone(); 150 | 151 | let client = use_context::>(cx); 152 | let client_clone = client.clone(); 153 | 154 | spawn_local_scoped(cx, async move { 155 | use_loading_context.set(Loading(true)); 156 | let _ = client.get().auth().sign_out().await; 157 | use_loading_context.set(Loading(false)); 158 | }); 159 | }) { "Sign Out" } 160 | } 161 | } 162 | } 163 | ) 164 | } 165 | } 166 | } 167 | 168 | fn main() { 169 | sycamore::render(|cx| view! { cx, App {} }); 170 | } 171 | -------------------------------------------------------------------------------- /examples/sycamore-guestbook/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | target 3 | Cargo.lock -------------------------------------------------------------------------------- /examples/sycamore-guestbook/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sycamore-guestbook" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | sycamore = { version = "0.8.2", features = ["suspense"] } 8 | supabase-js-rs = { path = "../../" } 9 | web-sys = "0.3.60" 10 | wasm-bindgen = "0.2.83" 11 | js-sys = "0.3.60" 12 | serde = { version = "1.0", features = ["derive"] } 13 | serde-wasm-bindgen = "0.4" -------------------------------------------------------------------------------- /examples/sycamore-guestbook/README.md: -------------------------------------------------------------------------------- 1 | # Guestbook with Sycamore and Supabase 2 | 3 | ```powershell 4 | $Env:SUPABASE_URL="https://xyzcompany.supabase.co"; $Env:SUPABASE_KEY="public-anon-key"; trunk serve --open 5 | ``` 6 | -------------------------------------------------------------------------------- /examples/sycamore-guestbook/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Sycamore Guestbook 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/sycamore-guestbook/src/main.rs: -------------------------------------------------------------------------------- 1 | use js_sys::{Array, Object, Reflect}; 2 | use serde::{Deserialize, Serialize}; 3 | use supabase_js_rs::{create_client, SupabaseClient}; 4 | use sycamore::{futures::spawn_local_scoped, prelude::*, suspense::Suspense, web::html::tr}; 5 | use wasm_bindgen::{JsValue, __rt::IntoJsResult}; 6 | use web_sys::console::log_1; 7 | 8 | #[derive(Serialize, Deserialize)] 9 | #[serde(rename_all = "camelCase")] 10 | struct OrderOptions { 11 | ascending: bool, 12 | } 13 | 14 | #[component] 15 | async fn Index(cx: Scope<'_>) -> View { 16 | let client: &RcSignal = use_context::>(cx); 17 | let res: Result = client 18 | .get() 19 | .from("messages") 20 | .select_(Some("*")) 21 | .order_( 22 | "id", 23 | serde_wasm_bindgen::to_value(&OrderOptions { ascending: false }).unwrap(), 24 | ) 25 | .limit(1) 26 | .await; 27 | 28 | let data: Array = Array::from(&Object::from( 29 | Reflect::get(&res.unwrap(), &"data".into_js_result().unwrap()).unwrap(), 30 | )); 31 | 32 | let messages: &Signal> = create_signal(cx, data.to_vec()); 33 | 34 | let message: &Signal = create_signal(cx, String::new()); 35 | let name: &Signal = create_signal(cx, String::new()); 36 | 37 | view! { 38 | cx, 39 | ul { 40 | Indexed( 41 | iterable=messages, 42 | view=|cx, message| view! { 43 | cx, 44 | li { (Reflect::get(&message, &"message".into_js_result().unwrap()).unwrap().as_string().unwrap()) } 45 | }, 46 | ) 47 | } 48 | 49 | p { "Message" } 50 | textarea(bind:value=message) 51 | 52 | p { "Name" } 53 | input(bind:value=name) 54 | 55 | button(on:click=move |_| { 56 | spawn_local_scoped(cx, async move { 57 | let post = Post { 58 | message: message.get().to_string(), 59 | name: name.get().to_string(), 60 | }; 61 | let res = client.get().from("messages").insert_(serde_wasm_bindgen::to_value(&post).unwrap()).select(Some("*")).await; 62 | let inserted = Array::from(&Object::from(Reflect::get(&res.unwrap(), &"data".into_js_result().unwrap()).unwrap())); 63 | messages.modify().push(inserted.get(0)); 64 | message.set("".to_string()); 65 | name.set("".to_string()); 66 | }); 67 | }) { "Submit" } 68 | } 69 | } 70 | 71 | #[derive(Serialize, Deserialize)] 72 | struct Post { 73 | message: String, 74 | name: String, 75 | } 76 | 77 | fn main() { 78 | let url = std::env!("SUPABASE_URL"); 79 | let key = std::env!("SUPABASE_KEY"); 80 | 81 | sycamore::render(|cx| { 82 | let client: RcSignal = create_rc_signal(create_client(url, key)); 83 | provide_context(cx, client); 84 | 85 | view! { 86 | cx, 87 | h1 { "Guestbook" } 88 | Suspense(fallback=view! { 89 | cx, 90 | "Loading..." 91 | }) { 92 | Index {} 93 | } 94 | } 95 | }); 96 | } 97 | -------------------------------------------------------------------------------- /examples/sycamore-realtime-chat/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | target 3 | Cargo.lock -------------------------------------------------------------------------------- /examples/sycamore-realtime-chat/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sycamore-realtime-chat" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | sycamore = { git = "https://github.com/sycamore-rs/sycamore", rev = "e402f11d733c59f02604f6d6ea97a0977c2d90d5" } 10 | supabase-js-rs = { path = "../../" } 11 | wasm-bindgen = "0.2.83" 12 | web-sys = "0.3.60" 13 | serde = { version = "1.0", features = ["derive"] } 14 | serde-wasm-bindgen = "0.4" -------------------------------------------------------------------------------- /examples/sycamore-realtime-chat/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Realtime chat with Sycamore and Supabase 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/sycamore-realtime-chat/src/main.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use supabase_js_rs::create_client; 3 | use sycamore::prelude::*; 4 | use wasm_bindgen::closure::Closure; 5 | use wasm_bindgen::JsValue; 6 | use web_sys::console::{log_1, log_2}; 7 | 8 | #[derive(Serialize, Deserialize)] 9 | pub struct RealtimePostgresChangesFilter { 10 | pub event: String, 11 | pub schema: String, 12 | } 13 | 14 | fn main() { 15 | let supabase_url = std::env!("SUPABASE_URL"); 16 | let supabase_key = std::env!("SUPABASE_KEY"); 17 | 18 | sycamore::render(|cx| { 19 | let client = create_rc_signal(create_client(supabase_url, supabase_key)); 20 | 21 | let payload = Closure::new(move |event: JsValue| { 22 | log_1(&event); 23 | }); 24 | 25 | let subscribition_callback: Closure = 26 | Closure::new(move |status: JsValue, error: JsValue| { 27 | log_2(&status, &error); 28 | }); 29 | 30 | let filter = RealtimePostgresChangesFilter { 31 | event: "*".to_string(), 32 | schema: "*".to_string(), 33 | }; 34 | 35 | client 36 | .get() 37 | .channel("*") 38 | .on( 39 | "postgres_changes", 40 | &serde_wasm_bindgen::to_value(&filter).unwrap(), 41 | &payload, 42 | ) 43 | .subscribe(Some(&subscribition_callback)); 44 | 45 | payload.forget(); 46 | subscribition_callback.forget(); 47 | 48 | view! { 49 | cx, 50 | div {"Real time chat"} 51 | } 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /examples/yew-supabase-auth/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | dist -------------------------------------------------------------------------------- /examples/yew-supabase-auth/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yew-supabase-auth" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | yew = { version = "0.20.0", features = ["csr"] } 8 | supabase-js-rs = { path = "../../" } 9 | wasm-bindgen = "0.2.83" 10 | web-sys = "0.3.60" 11 | js-sys = "0.3.60" -------------------------------------------------------------------------------- /examples/yew-supabase-auth/README.md: -------------------------------------------------------------------------------- 1 | # Supabase Auth with Yew 2 | 3 | This is example of [Supabase](https://supabase.com/) auth in application made with [Yew](https://yew.rs/). 4 | 5 | # How to run 6 | 7 | Install Trunk 8 | 9 | ``` 10 | cargo install --locked trunk 11 | ``` 12 | 13 | Run example 14 | 15 | ``` 16 | trunk serve 17 | ``` 18 | -------------------------------------------------------------------------------- /examples/yew-supabase-auth/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Supabase Auth with Yew 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/yew-supabase-auth/src/main.rs: -------------------------------------------------------------------------------- 1 | use js_sys::{JsString, Object, Reflect}; 2 | use std::rc::Rc; 3 | use supabase_js_rs::{self, Credentials, SupabaseClient}; 4 | use wasm_bindgen::{self, prelude::Closure, JsCast, JsValue, __rt::IntoJsResult}; 5 | use web_sys::{EventTarget, HtmlInputElement}; 6 | use yew::{platform::spawn_local, prelude::*}; 7 | 8 | #[derive(Debug, Clone, PartialEq)] 9 | pub struct Session { 10 | pub client: SupabaseClient, 11 | pub data: JsValue, 12 | } 13 | 14 | impl Default for Session { 15 | fn default() -> Self { 16 | let client = 17 | supabase_js_rs::create_client("https://xyzcompany.supabase.co", "public-anon-key"); 18 | Self { 19 | client, 20 | data: JsValue::NULL, 21 | } 22 | } 23 | } 24 | 25 | impl Session { 26 | pub async fn sign_in(&self, email: &str, password: &str) -> Result { 27 | let res = self 28 | .client 29 | .auth() 30 | .sign_in_with_password(Credentials { 31 | email: email.to_string(), 32 | password: password.to_string(), 33 | }) 34 | .await; 35 | Ok(res.unwrap()) 36 | } 37 | pub async fn sign_out(&self) -> JsValue { 38 | self.client.auth().sign_out().await.unwrap() 39 | } 40 | } 41 | 42 | impl Reducible for Session { 43 | type Action = JsValue; 44 | 45 | fn reduce(self: Rc, action: Self::Action) -> Rc { 46 | Session { 47 | client: self.client.clone(), 48 | data: action, 49 | } 50 | .into() 51 | } 52 | } 53 | 54 | pub type SessionContext = UseReducerHandle; 55 | 56 | #[derive(Debug, Properties, PartialEq)] 57 | pub struct SessionProviderProps { 58 | #[prop_or_default] 59 | pub children: Children, 60 | } 61 | 62 | #[function_component] 63 | pub fn SessionProvider(props: &SessionProviderProps) -> Html { 64 | let context: UseReducerHandle = use_reducer(|| Session::default()); 65 | let context_clone = context.clone(); 66 | let callback: Closure = Closure::new(move |_event, session| { 67 | context_clone.dispatch(session); 68 | }); 69 | context.client.auth().on_auth_state_change(&callback); 70 | callback.forget(); 71 | html! { 72 | {context}> 73 | {props.children.clone()} 74 | > 75 | } 76 | } 77 | 78 | #[function_component] 79 | fn Index() -> Html { 80 | let loading_state = use_state(|| false); 81 | let is_loading = loading_state.clone(); 82 | 83 | let error = use_state(|| JsValue::NULL); 84 | let error_value = error.clone(); 85 | 86 | let session: UseReducerHandle = use_context::().unwrap(); 87 | let data: JsValue = session.data.to_owned(); 88 | 89 | let email_input_ref = use_node_ref(); 90 | let email_input_handle = use_state(String::default); 91 | let email_input_value = (*email_input_handle).clone(); 92 | let email = email_input_value.clone(); 93 | 94 | let onchange = { 95 | let email_input_ref = email_input_ref.clone(); 96 | Callback::from(move |_| { 97 | let input = email_input_ref.cast::(); 98 | if let Some(input) = input { 99 | email_input_handle.set(input.value()); 100 | } 101 | }) 102 | }; 103 | 104 | let password_input_value_handle = use_state(|| String::default()); 105 | let password_input_value = (*password_input_value_handle).clone(); 106 | let password = password_input_value.clone(); 107 | 108 | let on_password_input_change: Callback = { 109 | let password_input_value_handle = password_input_value_handle.clone(); 110 | Callback::from(move |e: InputEvent| { 111 | let target: Option = e.target(); 112 | let input = target.and_then(|t| t.dyn_into::().ok()); 113 | if let Some(input) = input { 114 | password_input_value_handle.set(input.value()); 115 | } 116 | }) 117 | }; 118 | 119 | let use_session = session.clone(); 120 | let sign_out = { 121 | move |_| { 122 | let use_session = use_session.clone(); 123 | spawn_local(async move { 124 | use_session.sign_out().await; 125 | }); 126 | } 127 | }; 128 | 129 | let use_session = session.clone(); 130 | 131 | let sign_in = { 132 | move |_| { 133 | let use_session = use_session.clone(); 134 | let email = email.clone(); 135 | let password = password.clone(); 136 | let error_value = error_value.clone(); 137 | loading_state.set(true); 138 | let loading_clone = loading_state.clone(); 139 | spawn_local(async move { 140 | let sign_in_result = use_session.sign_in(&email, &password).await; 141 | let message = Reflect::get( 142 | &Reflect::get( 143 | &Object::from(sign_in_result.unwrap()), 144 | &"error".into_js_result().unwrap(), 145 | ) 146 | .unwrap(), 147 | &"message".into_js_result().unwrap(), 148 | ); 149 | error_value.set(message.unwrap()); 150 | loading_clone.clone().set(false); 151 | }); 152 | } 153 | }; 154 | 155 | let loading = is_loading.clone(); 156 | 157 | html! { 158 |
159 |

{"Yew with Supabase"}

160 | 161 | if data == JsValue::NULL { 162 | 163 | 169 | 170 | 176 | 177 | 180 | 181 | if *error != JsValue::NULL { 182 |

{error.as_string()}

183 | } 184 | 185 | } else { 186 |

{"Logged in"}

187 | {format!("{:#?}", data)} 188 | 190 | } 191 |
192 | } 193 | } 194 | 195 | #[function_component] 196 | fn App() -> Html { 197 | html! { 198 | 199 | 200 | 201 | } 202 | } 203 | 204 | fn main() { 205 | yew::Renderer::::new().render(); 206 | } 207 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! `supabase-js-rs` is a Rust bindings for Supabase JavaScript library via WebAssembly. 2 | //! 3 | 4 | use wasm_bindgen::prelude::*; 5 | 6 | /// Sign in with email and password credentials 7 | #[wasm_bindgen(getter_with_clone)] 8 | pub struct Credentials { 9 | pub email: String, 10 | pub password: String, 11 | } 12 | 13 | #[wasm_bindgen(getter_with_clone)] 14 | pub struct SignInWithOAuthCredentials { 15 | pub provider: String, 16 | pub options: JsValue, 17 | } 18 | 19 | #[wasm_bindgen(getter_with_clone)] 20 | pub struct CurrentSession { 21 | pub access_token: String, 22 | pub refresh_token: String, 23 | } 24 | 25 | /* 26 | #[wasm_bindgen(getter_with_clone)] 27 | pub struct MFAChallengeParams { 28 | pub factor_id: String, 29 | } 30 | */ 31 | 32 | /* 33 | #[wasm_bindgen(getter_with_clone)] 34 | pub struct MFAVerifyParams { 35 | pub factor_id: String, 36 | pub challenge_id: String, 37 | pub code: String, 38 | } 39 | */ 40 | 41 | #[wasm_bindgen] 42 | extern "C" { 43 | 44 | #[derive(Debug, Clone, PartialEq)] 45 | pub type SupabaseClient; 46 | 47 | /// # Create client 48 | /// 49 | #[wasm_bindgen(js_namespace = ["supabase"], js_name = createClient)] 50 | pub fn create_client(supabase_url: &str, supabase_key: &str) -> SupabaseClient; 51 | 52 | #[wasm_bindgen(method, js_name = from)] 53 | pub fn from(this: &SupabaseClient, table: &str) -> Database; 54 | 55 | pub type Database; 56 | 57 | #[wasm_bindgen(method, catch, js_name = select)] 58 | pub async fn select(this: &Database, columns: Option<&str>) -> Result; 59 | #[wasm_bindgen(method, js_name = select)] 60 | pub fn select_(this: &Database, columns: Option<&str>) -> Database; 61 | 62 | /// # Order the query 63 | /// 64 | /// Order query result by column. 65 | /// 66 | /// ```ignore 67 | /// #[derive(Serialize, Deserialize)] 68 | /// #[serde(rename_all = "camelCase")] 69 | /// struct OrderOptions { 70 | /// foreign_table: String, 71 | /// nulls_first: bool, 72 | /// ascending: bool, 73 | /// } 74 | /// let data: JsValue = client 75 | /// .get() 76 | /// .from("countries") 77 | /// .select_(Some("name, cities ( name )")) 78 | /// .order( 79 | /// "name", 80 | /// serde_wasm_bindgen::to_value(&OrderOptions { 81 | /// foreign_table: "cities".to_string(), 82 | /// nulls_first: false, 83 | /// ascending: true, 84 | /// }).unwrap(), 85 | /// ) 86 | /// .await.unwrap(); 87 | /// ``` 88 | /// 89 | #[wasm_bindgen(method, catch, js_name = order)] 90 | pub async fn order(this: &Database, column: &str, options: JsValue) 91 | -> Result; 92 | #[wasm_bindgen(method, js_name = order)] 93 | pub fn order_(this: &Database, column: &str, options: JsValue) -> Database; 94 | 95 | /// # Limit the query 96 | /// 97 | /// Limit the query result by count. 98 | /// 99 | #[wasm_bindgen(method, catch, js_name = limit)] 100 | pub async fn limit(this: &Database, count: u32) -> Result; 101 | #[wasm_bindgen(method, js_name = limit)] 102 | pub fn limit_(this: &Database, count: u32) -> Database; 103 | 104 | /// # Limit the query to a range 105 | /// 106 | /// Limit the query result by from and to inclusively. 107 | /// 108 | #[wasm_bindgen(method, catch, js_name = range)] 109 | pub async fn range(this: &Database, from: u32, to: u32) -> Result; 110 | #[wasm_bindgen(method, js_name = range)] 111 | pub fn range_(this: &Database, from: u32, to: u32) -> Database; 112 | 113 | /// # Retrieve the query as one row 114 | /// 115 | /// Return data as a single object instead of an array of objects. 116 | /// 117 | #[wasm_bindgen(method, catch, js_name = single)] 118 | pub async fn single(this: &Database) -> Result; 119 | 120 | /// # Retrieve the query as 0-1 rows 121 | /// 122 | /// Return data as a single object instead of an array of objects. 123 | /// 124 | #[wasm_bindgen(method, catch, js_name = maybeSingle)] 125 | pub async fn maybe_single(this: &Database) -> Result; 126 | 127 | /// # Retrieve the query as a CSV string 128 | /// 129 | /// Return data as a string in CSV format. 130 | /// 131 | /// ```ignore 132 | /// let csv = client.get().from("countries").select_(Some("*")).csv().await.unwrap(); 133 | /// ``` 134 | /// 135 | #[wasm_bindgen(method, catch, js_name = csv)] 136 | pub async fn csv(this: &Database) -> Result; 137 | 138 | /// # Column is equal to a value 139 | /// 140 | /// Match only rows where column is equal to value. 141 | /// 142 | #[wasm_bindgen(method, catch, js_name = eq)] 143 | pub async fn eq(this: &Database, column: &str, value: &JsValue) -> Result; 144 | #[wasm_bindgen(method, js_name = eq)] 145 | pub fn eq_(this: &Database, column: &str, value: &JsValue) -> Database; 146 | 147 | /// # Column is not equal to a value 148 | /// 149 | /// Match only rows where column is not equal to value. 150 | /// 151 | #[wasm_bindgen(method, catch, js_name = neq)] 152 | pub async fn neq(this: &Database, column: &str, value: &JsValue) -> Result; 153 | #[wasm_bindgen(method, js_name = neq)] 154 | pub fn neq_(this: &Database, column: &str, value: &JsValue) -> Database; 155 | 156 | /// # Column is greater than a value 157 | /// 158 | /// Match only rows where column is greater than value. 159 | /// 160 | #[wasm_bindgen(method, catch, js_name = gt)] 161 | pub async fn gt(this: &Database, column: &str, value: &JsValue) -> Result; 162 | #[wasm_bindgen(method, js_name = gt)] 163 | pub fn gt_(this: &Database, column: &str, value: &JsValue) -> Database; 164 | 165 | /// # Column is greater than or equal to a value 166 | /// 167 | /// Match only rows where column is greater than or equal to value. 168 | /// 169 | #[wasm_bindgen(method, catch, js_name = gte)] 170 | pub async fn gte(this: &Database, column: &str, value: &JsValue) -> Result; 171 | #[wasm_bindgen(method, js_name = gte)] 172 | pub fn gte_(this: &Database, column: &str, value: &JsValue) -> Database; 173 | 174 | /// # Column is less than a value 175 | /// 176 | /// Match only rows where column is less than value. 177 | /// 178 | #[wasm_bindgen(method, catch, js_name = lt)] 179 | pub async fn lt(this: &Database, column: &str, value: &JsValue) -> Result; 180 | #[wasm_bindgen(method, js_name = lt)] 181 | pub fn lt_(this: &Database, column: &str, value: &JsValue) -> Database; 182 | 183 | /// # Column is less than or equal to a value 184 | /// 185 | /// Match only rows where column is less than or equal to value. 186 | /// 187 | #[wasm_bindgen(method, catch, js_name = lte)] 188 | pub async fn lte(this: &Database, column: &str, value: &JsValue) -> Result; 189 | #[wasm_bindgen(method, js_name = lte)] 190 | pub fn lte_(this: &Database, column: &str, value: &JsValue) -> Database; 191 | 192 | /// # Column matches a pattern 193 | /// 194 | /// Match only rows where column matches pattern case-sensitively. 195 | /// 196 | #[wasm_bindgen(method, catch, js_name = like)] 197 | pub async fn like(this: &Database, column: &str, pattern: &str) -> Result; 198 | #[wasm_bindgen(method, js_name = like)] 199 | pub fn like_(this: &Database, column: &str, pattern: &str) -> Database; 200 | 201 | /// # Column matches a case-insensitive pattern 202 | /// 203 | /// Match only rows where column matches pattern case-insensitively. 204 | /// 205 | /// ```ignore 206 | /// client.from("countries").select(None).ilike(&"name", &"%alba%").await; 207 | /// ``` 208 | /// 209 | #[wasm_bindgen(method, catch, js_name = ilike)] 210 | pub async fn ilike(this: &Database, column: &str, pattern: &str) -> Result; 211 | #[wasm_bindgen(method, js_name = ilike)] 212 | pub fn ilike_(this: &Database, column: &str, pattern: &str) -> Database; 213 | 214 | /// # Column is a value 215 | /// 216 | /// Match only rows where column IS value. 217 | /// 218 | /// ```ignore 219 | /// // check for nullness 220 | /// client.from("countries").select(None).is("name", JsValue::NULL); 221 | /// // or check for true of false 222 | /// client.from("countries").select(None).is("name", JsValue::TRUE); 223 | /// ``` 224 | /// 225 | #[wasm_bindgen(method, catch, js_name = is)] 226 | pub async fn is(this: &Database, column: &str, value: &JsValue) -> Result; 227 | #[wasm_bindgen(method, js_name = is)] 228 | pub fn is_(this: &Database, column: &str, value: &JsValue) -> Database; 229 | 230 | /// # Column is in an array 231 | /// 232 | /// Match only rows where column is included in the values array. 233 | /// 234 | #[wasm_bindgen(method, catch, js_name = in)] 235 | pub async fn r#in( 236 | this: &Database, 237 | column: &str, 238 | values: Vec, 239 | ) -> Result; 240 | #[wasm_bindgen(method, js_name = in)] 241 | pub fn r#in_(this: &Database, column: &str, values: Vec) -> Database; 242 | 243 | /// # Column contains every element in a value 244 | /// 245 | /// Only relevant for jsonb, array, and range columns. Match only rows where column contains every element appearing in value. 246 | /// 247 | #[wasm_bindgen(method, catch, js_name = contains)] 248 | pub async fn contains( 249 | this: &Database, 250 | column: &str, 251 | value: JsValue, 252 | ) -> Result; 253 | #[wasm_bindgen(method, js_name = contains)] 254 | pub fn contains_(this: &Database, column: &str, value: JsValue) -> Database; 255 | 256 | /// # Contained by value 257 | /// 258 | /// Only relevant for jsonb, array, and range columns. Match only rows where every element appearing in column is contained by value. 259 | /// 260 | #[wasm_bindgen(method, catch, js_name = containedBy)] 261 | pub async fn contained_by( 262 | this: &Database, 263 | column: &str, 264 | value: JsValue, 265 | ) -> Result; 266 | #[wasm_bindgen(method, js_name = containedBy)] 267 | pub fn contained_by_(this: &Database, column: &str, value: JsValue) -> Database; 268 | 269 | /// # Greater than a range 270 | /// 271 | /// Only relevant for range columns. Match only rows where every element in column is greater than any element in range. 272 | /// 273 | #[wasm_bindgen(method, catch, js_name = rangeGt)] 274 | pub async fn range_gt(this: &Database, column: &str, range: &str) -> Result; 275 | #[wasm_bindgen(method, js_name = rangeGt)] 276 | pub fn range_gt_(this: &Database, column: &str, range: &str) -> Database; 277 | 278 | /// # Greater than or equal to a range 279 | /// 280 | /// Only relevant for range columns. Match only rows where every element in column is either contained in range or greater than any element in range. 281 | /// 282 | #[wasm_bindgen(method, catch, js_name = rangeGte)] 283 | pub async fn range_gte(this: &Database, column: &str, range: &str) -> Result; 284 | #[wasm_bindgen(method, js_name = rangeGte)] 285 | pub fn range_gte_(this: &Database, column: &str, range: &str) -> Database; 286 | 287 | /// # Less than a range 288 | /// 289 | /// Only relevant for range columns. Match only rows where every element in column is less than any element in range. 290 | /// 291 | #[wasm_bindgen(method, catch, js_name = rangeLt)] 292 | pub async fn range_lt(this: &Database, column: &str, range: &str) -> Result; 293 | #[wasm_bindgen(method, js_name = rangeLt)] 294 | pub fn range_lt_(this: &Database, column: &str, range: &str) -> Database; 295 | 296 | /// # Less than or equal to a range 297 | /// 298 | /// Only relevant for range columns. Match only rows where every element in column is either contained in range or less than any element in range. 299 | /// 300 | #[wasm_bindgen(method, catch, js_name = rangeLte)] 301 | pub async fn range_lte(this: &Database, column: &str, range: &str) -> Result; 302 | #[wasm_bindgen(method, js_name = rangeLte)] 303 | pub fn range_lte_(this: &Database, column: &str, range: &str) -> Database; 304 | 305 | /// # Mutually exclusive to a range 306 | /// 307 | /// Only relevant for range columns. Match only rows where column is mutually exclusive to range and there can be no element between the two ranges. 308 | /// 309 | #[wasm_bindgen(method, catch, js_name = rangeAdjacent)] 310 | pub async fn range_adjacent( 311 | this: &Database, 312 | column: &str, 313 | range: &str, 314 | ) -> Result; 315 | #[wasm_bindgen(method, js_name = rangeAdjacent)] 316 | pub fn range_adjacent_(this: &Database, column: &str, range: &str) -> Database; 317 | 318 | /// # With a common element 319 | /// 320 | /// Only relevant for array and range columns. Match only rows where column and value have an element in common. 321 | /// 322 | #[wasm_bindgen(method, catch, js_name = overlaps)] 323 | pub async fn overlaps( 324 | this: &Database, 325 | column: &str, 326 | value: JsValue, 327 | ) -> Result; 328 | #[wasm_bindgen(method, js_name = overlaps)] 329 | pub fn overlaps_(this: &Database, column: &str, value: JsValue) -> Database; 330 | 331 | /// # Match a string 332 | /// 333 | /// Only relevant for text and tsvector columns. Match only rows where column matches the query string in query. 334 | /// 335 | #[wasm_bindgen(method, catch, js_name = textSearch)] 336 | pub async fn text_search( 337 | this: &Database, 338 | column: &str, 339 | query: &str, 340 | options: JsValue, 341 | ) -> Result; 342 | #[wasm_bindgen(method, js_name = textSearch)] 343 | pub fn text_search_(this: &Database, column: &str, query: &str, options: JsValue) -> Database; 344 | 345 | /// # Update data 346 | /// 347 | /// Perform an UPDATE on the table or view. 348 | /// 349 | #[wasm_bindgen(method, catch, js_name = update)] 350 | pub async fn update(this: &Database, values: &JsValue) -> Result; 351 | #[wasm_bindgen(method, js_name = update)] 352 | pub fn update_(this: &Database, values: &JsValue) -> Database; 353 | 354 | /// # Upsert data 355 | /// 356 | /// Perform an UPSERT on the table or view. 357 | /// 358 | #[wasm_bindgen(method, js_name = upsert)] 359 | pub fn upsert(this: &Database, values: JsValue) -> Database; 360 | 361 | /// # Delete data 362 | /// 363 | /// Should always be combined with filters 364 | /// 365 | /// ```ignore 366 | /// let client = supabase_js_rs::create_client("https://xyzcompany.supabase.co", "public-anon-key"); 367 | /// let res: Result = client.from("countries").delete().eq("id", 1.into_js_result().unwrap()).await; 368 | /// ``` 369 | /// 370 | #[wasm_bindgen(method, js_name = delete)] 371 | pub fn delete(this: &Database) -> Database; 372 | 373 | /// # Insert data 374 | /// 375 | /// Perform an INSERT into the table or view. 376 | /// 377 | #[wasm_bindgen(method, catch, js_name = insert)] 378 | pub async fn insert(this: &Database, values: JsValue) -> Result; 379 | #[wasm_bindgen(method, js_name = insert)] 380 | pub fn insert_(this: &Database, values: JsValue) -> Database; 381 | 382 | /// Auth methods 383 | #[wasm_bindgen(method, getter = auth)] 384 | pub fn auth(this: &SupabaseClient) -> Auth; 385 | 386 | pub type Auth; 387 | 388 | /// # Sign in anonymously 389 | /// 390 | #[wasm_bindgen(method, catch, js_name = signInAnonymously)] 391 | pub async fn sign_in_anonymously(this: &Auth) -> Result; 392 | 393 | /// # Create a new user 394 | /// 395 | #[wasm_bindgen(method, catch, js_name = signUp)] 396 | pub async fn sign_up(this: &Auth, credentials: Credentials) -> Result; 397 | 398 | /// # Sign in a user 399 | /// 400 | #[wasm_bindgen(method, catch, js_name = signInWithPassword)] 401 | pub async fn sign_in_with_password( 402 | this: &Auth, 403 | credentials: Credentials, 404 | ) -> Result; 405 | 406 | /// # Sign in a user through OTP 407 | /// 408 | /// Log in a user using magiclink or a one-time password (OTP). 409 | /// 410 | #[wasm_bindgen(method, catch, js_name = signInWithOtp)] 411 | pub async fn sign_in_with_otp(this: &Auth, credentials: JsValue) -> Result; 412 | 413 | /// # Sign in a user through OAuth 414 | /// 415 | /// Log in an existing user via a third-party provider. 416 | /// 417 | #[wasm_bindgen(method, catch, js_name = signInWithOAuth)] 418 | pub async fn sign_in_with_oauth( 419 | this: &Auth, 420 | credentials: SignInWithOAuthCredentials, 421 | ) -> Result; 422 | 423 | /// # Sign out a user 424 | /// 425 | #[wasm_bindgen(method, catch, js_name = signOut)] 426 | pub async fn sign_out(this: &Auth) -> Result; 427 | 428 | /// # Retrieve a session 429 | /// 430 | /// Returns the session, refreshing it if necessary. 431 | #[wasm_bindgen(method, catch, js_name = getSession)] 432 | pub async fn get_session(this: &Auth) -> Result; 433 | 434 | /// # Retrieve a new session 435 | /// 436 | /// Returns a new session, regardless of expiry status. 437 | #[wasm_bindgen(method, catch, js_name = refreshSession)] 438 | pub async fn refresh_session(this: &Auth) -> Result; 439 | 440 | /// # Retrieve a user 441 | /// 442 | /// Takes in an optional access token jwt or get the jwt from the current session. 443 | #[wasm_bindgen(method, catch, js_name = getUser)] 444 | pub async fn get_user(this: &Auth, jwt: Option<&str>) -> Result; 445 | 446 | /// # Update user 447 | /// 448 | /// Updates user data, if there is a logged in user. 449 | /// 450 | #[wasm_bindgen(method, catch, js_name = updateUser)] 451 | pub async fn update_user(this: &Auth, attributes: JsValue) -> Result; 452 | 453 | #[wasm_bindgen(method, catch, js_name = setSession)] 454 | pub async fn set_session( 455 | this: &Auth, 456 | current_session: CurrentSession, 457 | ) -> Result; 458 | 459 | /// Listen to auth events 460 | /// 461 | /// # Example 462 | /// 463 | /// ```ignore 464 | /// let client = supabase_js_rs::create_client("SUPABASE_URL", "SUPABASE_ANON_KEY"); 465 | /// let auth_event_callback: Closure = Closure::new(move |event: JsValue, session: JsValue| { 466 | /// 467 | /// }); 468 | /// client.auth().on_auth_state_change(&auth_event_callback); 469 | /// auth_event_callback.forget(); 470 | /// ``` 471 | #[wasm_bindgen(method, js_name = onAuthStateChange)] 472 | pub fn on_auth_state_change(this: &Auth, callback: &Closure); 473 | 474 | /// # Send a password reset request 475 | /// 476 | /// Sends a password reset request to an email address. 477 | /// 478 | #[wasm_bindgen(method, catch, js_name = resetPasswordForEmail)] 479 | pub async fn reset_password_for_email( 480 | this: &Auth, 481 | email: &str, 482 | options: JsValue, 483 | ) -> Result; 484 | 485 | /* 486 | pub type Mfa; 487 | 488 | #[wasm_bindgen(method, getter = mfa)] 489 | pub fn mfa(this: &Auth) -> Mfa; 490 | 491 | /// Create a challenge 492 | #[wasm_bindgen(method, catch, js_name = challenge)] 493 | pub fn challenge(this: &Mfa, params: MFAChallengeParams) -> Result; 494 | 495 | /// Verify a challenge 496 | #[wasm_bindgen(method, catch, js_name = verify)] 497 | pub fn verify(this: &Mfa, params: MFAVerifyParams) -> Result; 498 | */ 499 | 500 | #[wasm_bindgen(method, js_name = channel)] 501 | pub fn channel(this: &SupabaseClient, name: &str) -> RealtimeChannel; 502 | 503 | /// # Unsubscribe from all channels 504 | /// 505 | #[wasm_bindgen(method, js_name = removeAllChannels)] 506 | pub fn remove_all_channels(this: &SupabaseClient); 507 | 508 | /// # Retrieve all channels 509 | /// 510 | #[wasm_bindgen(method, js_name = getChannels)] 511 | pub fn get_channels(this: &SupabaseClient) -> JsValue; 512 | 513 | pub type RealtimeChannel; 514 | 515 | /// # Subscribe to database changes 516 | /// 517 | #[wasm_bindgen(method, js_name = on)] 518 | pub fn on( 519 | this: &RealtimeChannel, 520 | r#type: &str, 521 | filter: &JsValue, 522 | callback: &Closure, 523 | ) -> RealtimeChannel; 524 | 525 | #[wasm_bindgen(method, js_name = subscribe)] 526 | pub fn subscribe( 527 | this: &RealtimeChannel, 528 | callback: Option<&Closure>, 529 | ) -> RealtimeChannel; 530 | 531 | #[wasm_bindgen(method, js_name = storage)] 532 | pub fn storage(this: &SupabaseClient) -> Storage; 533 | 534 | pub type Storage; 535 | 536 | /// # Create a bucket 537 | /// 538 | /// Creates a new Storage bucket 539 | /// 540 | #[wasm_bindgen(method, catch, js_name = createBucket)] 541 | pub async fn create_bucket(this: &Storage, id: &str) -> Result; 542 | 543 | /// # Retrieve a bucket 544 | /// 545 | /// Retrieves the details of an existing Storage bucket. 546 | /// 547 | #[wasm_bindgen(method, catch, js_name = getBucket)] 548 | pub async fn get_bucket(this: &Storage, id: &str) -> Result; 549 | 550 | /// # List all buckets 551 | /// 552 | /// Retrieves the details of all Storage buckets within an existing project. 553 | /// 554 | #[wasm_bindgen(method, catch, js_name = listBuckets)] 555 | pub async fn list_buckets(this: &Storage) -> Result; 556 | 557 | /// # Update a bucket 558 | /// 559 | /// Updates a Storage bucket 560 | /// 561 | #[wasm_bindgen(method, catch, js_name = updateBucket)] 562 | pub async fn update_bucket(this: &Storage, options: JsValue) -> Result; 563 | 564 | /// # Empty a bucket 565 | /// 566 | /// Removes all objects inside a single bucket. 567 | /// 568 | #[wasm_bindgen(method, catch, js_name = emptyBucket)] 569 | pub async fn empty_bucket(this: &Storage, id: &str) -> Result; 570 | 571 | /// # Delete a bucket 572 | /// 573 | /// Deletes an existing bucket. A bucket can't be deleted with existing objects inside it. 574 | /// 575 | #[wasm_bindgen(method, catch, js_name = deleteBucket)] 576 | pub async fn delete_bucket(this: &Storage, id: &str) -> Result; 577 | 578 | } 579 | --------------------------------------------------------------------------------