CRUD Operations with Rust and Firebase (Realtime Database) 🦀+🔥
November 18, 2022 ¿Ves algún error? Corregir artículo
I decided to write this article because I didn't find content in Spanish for integrating Firebase with Rust, but in the process I encountered a big challenge.
- Firebase doesn't have an SDK package for Rust
So there was only one option - use the Realtime Database REST API that Firebase provides. Luckily for me, someone had already created a package that helps communicate with that API.
So I got to work creating these functions.
Packages We'll Use:
I'll add the Cargo file so you know what dependencies the project uses.
~[package] name = "rust-firebase" version = "0.1.0" edition = "2021" [dependencies] firebase-rs = "2.0.5" serde = "1.0.147" serde_json = "1.0" tokio = { version = "1.21.2", features = ["full"] }
Creating the Structures We'll Use:
For this example, we'll use two simple structures - a Response and a User.
~use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] struct User { name: String, age: u32, email: String, } #[derive(Serialize, Deserialize, Debug)] struct Response { name: String, }
Now the main Function:
We'll make our main function Async to wait for API responses using the Tokio package.
~use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] struct User { name: String, age: u32, email: String, } #[derive(Serialize, Deserialize, Debug)] struct Response { name: String, } #[tokio::main] async fn main() { }
Initializing a Firebase Client
Now we'll create a firebase client with our database URL that we'll use to pass it in the functions.
Note 📝: If you want to use Auth, replace the new function with auth and add your Auth token as the second parameter
~use firebase_rs::*; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] struct User { name: String, age: u32, email: String, } #[derive(Serialize, Deserialize, Debug)] struct Response { name: String, } #[tokio::main] async fn main() { let firebase = Firebase::new("https://myproject.firebaseio.com").unwrap(); }
We'll Create Two Functions That Will Help Us Parse:
~// Convert a string to a Response fn string_to_reponse(s: &str) -> Response { serde_json::from_str(s).unwrap() } // Convert a string to a User fn string_to_user(s: &str) -> User { serde_json::from_str(s).unwrap() }
We'll Create the Corresponding Methods:
Create a User:
~async fn set_user(firebase_client: &Firebase, user: &User) -> Response { let firebase = firebase_client.at("users"); let _users = firebase.set::<User>(&user).await; return string_to_reponse(&_users.unwrap().data); }
Get All Users:
~async fn get_users(firebase_client: &Firebase) -> HashMap<String,User> { let firebase = firebase_client.at("users"); let users = firebase.get::<HashMap<String, User>>().await; println!("{:?}", users); return users.unwrap(); }
Get a User by ID:
~async fn get_user(firebase_client: &Firebase, id: &String) -> User { let firebase = firebase_client.at("users").at(&id); let user = firebase.get::<User>().await; return user.unwrap(); }
Update a User:
~async fn update_user(firebase_client: &Firebase, id: &String, user: &User) -> User { let firebase = firebase_client.at("users").at(&id); let _user = firebase.update::<User>(&user).await; return string_to_user(&_user.unwrap().data); }
Delete a User:
~async fn delete_user(firebase_client: &Firebase, id: &String) { let firebase = firebase_client.at("users").at(&id); let _result = firebase.delete().await; }
Using Our Functions and Completing main:
For this process we'll create a user, then get all users in the database, then get only the created user, then update it, and finally delete it.
~use std::collections::HashMap; use firebase_rs::*; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] struct User { name: String, age: u32, email: String, } #[derive(Serialize, Deserialize, Debug)] struct Response { name: String, } #[tokio::main] async fn main() { // Create the user let user = User { name: "Jhon Doe".to_string(), age: 25, email: "jhon.doe@mail.com".to_string(), }; // Create the Firebase Instance let firebase = Firebase::new("https://myproject.firebaseio.com").unwrap(); // Create the user let response = set_user(&firebase, &user).await; // Get all users let users = get_users(&firebase).await; println!("{:?}", users); // Get the user let mut user = get_user(&firebase, &response.name).await; println!("{:?}", user); // Update the user user.email = "updated.mail@gmail.com".to_string(); let updated_user = update_user(&firebase, &response.name, &user).await; println!("{:?}", updated_user); // Delete the user delete_user(&firebase, &response.name).await; println!("User deleted"); } // Create a user async fn set_user(firebase_client: &Firebase, user: &User) -> Response { let firebase = firebase_client.at("users"); let _users = firebase.set::<User>(&user).await; return string_to_reponse(&_users.unwrap().data); } // Get All users async fn get_users(firebase_client: &Firebase) -> HashMap<String,User> { let firebase = firebase_client.at("users"); let users = firebase.get::<HashMap<String, User>>().await; println!("{:?}", users); return users.unwrap(); } // Get a user async fn get_user(firebase_client: &Firebase, id: &String) -> User { let firebase = firebase_client.at("users").at(&id); let user = firebase.get::<User>().await; return user.unwrap(); } // Update a user async fn update_user(firebase_client: &Firebase, id: &String, user: &User) -> User { let firebase = firebase_client.at("users").at(&id); let _user = firebase.update::<User>(&user).await; return string_to_user(&_user.unwrap().data); } async fn delete_user(firebase_client: &Firebase, id: &String) { let firebase = firebase_client.at("users").at(&id); let _result = firebase.delete().await; } // Convert a string to a Response fn string_to_reponse(s: &str) -> Response { serde_json::from_str(s).unwrap() } // Convert a string to a User fn string_to_user(s: &str) -> User { serde_json::from_str(s).unwrap() }
File Structure:
My Conclusions:
Firebase and Rust are not good friends yet. There's a lot to improve in this stack - from the lack of an official package to the fact that you can only use Realtime Database makes using Firebase not a good idea since you don't take advantage of all the benefits that Firebase gives you when using it completely.
I wouldn't use it in production until there's an official package.
Project Repository