51 - Implementación de funcionalidades clave en un microservicio en Rust

51 - Implementación de funcionalidades clave en un microservicio en Rust

¡Bienvenidos de nuevo a Rustaceo.es! Ahora que hemos aprendido a crear un microservicio en Rust, es hora de agregarle funcionalidades clave para que sea completamente funcional. En esta entrega, implementaremos métodos CRUD, manejo de errores, autenticación y paginación.


🚀 Funcionalidades esenciales en un microservicio #

Para que nuestro microservicio sea completo, debe incluir: ✔ Manejo de datos con operaciones CRUD (Create, Read, Update, Delete).Autenticación con JWT para proteger los endpoints.Paginación y filtrado de datos.Manejo de errores para respuestas más claras.Logging estructurado para monitoreo.


🏗 Implementando operaciones crud #

Nuestra API permitirá gestionar Pokémon en una base de datos PostgreSQL usando SQLx.

📌 1️⃣ Crear un nuevo pokémon (post) #

use actix_web::{web, HttpResponse, Responder};
use serde::Deserialize;
use sqlx::PgPool;

#[Derive(deserialize)]
struct NuevoElemento {
    nombre: String,
    tipo: String,
}

async fn agregar_pokemon(pool: web::Data<PgPool>, pokemon: web::Json<NuevoElemento>) -> impl Responder {
    let resultado = sqlx::query!("INSERT INTO pokemons (nombre, tipo) VALUES ($1, $2)",
        pokemon.nombre, pokemon.tipo)
        .execute(pool.get_ref())
        .await;
    
    match resultado {
        Ok(_) => HttpResponse::Created().finish(),
        Err(_) => HttpResponse::InternalServerError().finish(),
    }
}

Inserta datos en la base de datos usando SQLx.Responde con código 201 Created si el Pokémon fue agregado con éxito.

📌 2️⃣ Listar pokémon con paginación (get) #

async fn listar_pokemon(pool: web::Data<PgPool>, params: web::Query<Paginacion>) -> impl Responder {
    let offset = params.offset.unwrap_or(0);
    let limit = params.limit.unwrap_or(10);
    let pokemones = sqlx::query!("SELECT * FROM elementoes LIMIT $1 OFFSET $2", limit, offset)
        .fetch_all(pool.get_ref())
        .await;
    
    match elementoes {
        Ok(elementoes) => HttpResponse::Ok().json(elementoes),
        Err(_) => HttpResponse::InternalServerError().finish(),
    }
}

Soporta paginación con limit y offset.Retorna la lista de Pokémon almacenados en la base de datos.

📌 3️⃣ Actualizar un pokémon (put) #

async fn actualizar_elemento(pool: web::Data<PgPool>, id: web::Path<i32>, pokemon: web::Json<NuevoElemento>) -> impl Responder {
    let resultado = sqlx::query!("UPDATE elementoes SET nombre = $1, tipo = $2 WHERE id = $3",
        pokemon.nombre, pokemon.tipo, id.into_inner())
        .execute(pool.get_ref())
        .await;
    
    match resultado {
        Ok(_) => HttpResponse::Ok().finish(),
        Err(_) => HttpResponse::InternalServerError().finish(),
    }
}

Actualiza los datos de un Pokémon existente en la base de datos.Maneja errores en caso de que el Pokémon no exista.

📌 4️⃣ Eliminar un pokémon (delete) #

async fn eliminar_elemento(pool: web::Data<PgPool>, id: web::Path<i32>) -> impl Responder {
    let resultado = sqlx::query!("DELETE FROM elementoes WHERE id = $1", id.into_inner())
        .execute(pool.get_ref())
        .await;
    
    match resultado {
        Ok(_) => HttpResponse::NoContent().finish(),
        Err(_) => HttpResponse::InternalServerError().finish(),
    }
}

Elimina un Pokémon de la base de datos.Responde con código 204 No Content en caso de éxito.


🔐 Implementando autenticación con jwt #

Para proteger nuestra API, agregamos autenticación con JSON Web Tokens (JWT).

📌 Ejemplo de Generación y Validación de Tokens

use jsonwebtoken::{decode, encode, Header, Validation, EncodingKey, DecodingKey};
use serde::{Serialize, Deserialize};

#[Derive(serialize, deserialize)]
struct Claims {
    sub: String,
    exp: usize,
}

fn generar_token(usuario: &str) -> String {
    let claims = Claims {
        sub: usuario.to_string(),
        exp: 10000000000,
    };
    encode(&Header::default(), &claims, &EncodingKey::from_secret("mi_secreto".as_ref())).unwrap()
}

Generación de tokens JWT para autenticación.Uso de claves secretas para validar los tokens.


🛠 Manejo de errores y logging #

Para mejorar la depuración y monitoreo, usamos tracing para generar logs estructurados.

📌 Ejemplo de Logging con tracing:

use tracing::{info, error};

fn main() {
    info!("Microservicio iniciado");
    error!("Error crítico detectado");
}

Registro de logs detallados para facilitar la observabilidad.Integración con Prometheus y Grafana para monitoreo.


🏆 Conclusión #

Hemos agregado funcionalidades clave a nuestro microservicio en Rust:

🎯 Resumen de lo aprendido: ✔ Implementación de operaciones CRUD con SQLx. ✔ Paginación y filtrado en consultas a la base de datos. ✔ Autenticación con JWT para proteger endpoints. ✔ Logging estructurado con tracing para depuración y monitoreo.

🔮 Próximo paso: Desplegar el microservicio en un entorno de producción usando Docker y Kubernetes.

¡Nos vemos en la siguiente entrega, Rustaceos!