Operaciones CRUD con Rust y Firebase (Realtime Database) 🦀+🔥

18 de Noviembre del 2022 ¿Ves algún error? Corregir artículo rust-wallpaper

Decidí realizar este artículo debido a que no encontre contenido en español para integrar Firebase junto con Rust, pero en el proceso me encontre con un gran reto.

  • Firebase no tiene un Paquete de SDK para Rust

Entonces solo quedaba una opción usar el REST API de Realtime Database que proporcina Firebase, para mi suerte alguien ya había realizado un paquete que ayuda a comunicarse con dicho API.

Entonces me puse manos a la obra para crear esas funciones.

La clave para ser un buen programador es mantenerse en constante aprendizaje. 👉🏽

Paquetes que usaremos:

Agregaré el archivo de Cargo para que sepan que dependencias usa el proyecto.

~
[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"] }

Creando las estructuras que usaremos:

Para este ejemplo usaremos dos estructuras simple un Response y un 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, }

Ahora la función main:

Haremos que nuestra main function sea Async para esperar las respuesta del API usando el paquete Tokio.

~
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() { }

Inicializando un Cliente de firebase

Ahora crearemos un cliente de firebase con nuestra URL de la base de datos que usaremos para pasarlo en las funciones.

Nota 📝: Si deseas usar Auth remplaza la función new por auth y agrega tu Auth token como segundo parámetro

~
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(); }

Crearemos dos funciones que nos ayudarán a parsear:

~
// 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() }

Crearemos los métodos correspondientes:

Crear un usuario:

~
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); }

Obtener todos los usuarios:

~
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(); }

Obtener un usuario por 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(); }

Actualizar un usuario:

~
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); }

Eliminar un usuario:

~
async fn delete_user(firebase_client: &Firebase, id: &String) { let firebase = firebase_client.at("users").at(&id); let _result = firebase.delete().await; }

Usando nuestras funciones y completando el main:

Para este proceso crearemos un usuario luego obtendremos todos los usuarios en la base de datos despues obtendremos solo el usuario creado luego lo actualizaremos y al final lo eliminaremos.

~
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() }

Estructura de archivos:

src

main.rs

.gitignore

Cargo.toml

Cargo.lock

README.md

Mis conclusiones:

Firebase y Rust no son buenos amigos aun hay mucho que mejorar en este stack desde la falta de un paquete oficial hasta el hecho de solo poder usar Realtime Database hace que usar Firebase no sea buena idea ya que no se aprovechan todos los beneficios que Firebase te otorga al usarlo de manera completa.

Yo no lo usaría en producción hasta que no hubiese un paquete oficial.

Repositorio del Proyecto
Conviértete en un Go Ninja 🥷.Suscríbete a mi newsletter y recibe las últimas novedades en Go.