50 - Construcción de un microservicio en Rust - primeros pasos

50 - Construcción de un microservicio en Rust - primeros pasos

¡Bienvenidos de nuevo a Rustaceo.es! Hoy daremos nuestros primeros pasos en la construcción de un microservicio en Rust, aprovechando la seguridad y el alto rendimiento del lenguaje. En esta entrega, aprenderemos cómo configurar un microservicio básico usando Actix-Web, gestionar datos con SQLx y conectar nuestro servicio con una base de datos PostgreSQL.


🚀 ¿Qué necesitamos para empezar? #

Antes de comenzar, asegurémonos de contar con lo siguiente: ✔ Rust y Cargo instalados (rustup update para actualizar). ✔ PostgreSQL configurado y en ejecución.Conocimientos básicos de HTTP y APIs REST.

📌 Instalación de dependencias #

Creamos un nuevo proyecto en Rust y agregamos las dependencias necesarias:

cargo new Rust-microservice --bin
cd Rust-microservice

Editamos Cargo.toml para incluir:

[dependencies]
actix-web = "4"
sqlx = { version = "0.6", features = ["postgres", "runtime-tokio-native-tls"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
dotenv = "0.15"

Ejecutamos cargo check para verificar que todo esté correctamente configurado.


🏗 Creando un servidor http con actix-web #

Actix-Web es un framework asíncrono que nos permite manejar peticiones HTTP de manera eficiente. Creemos nuestro primer servidor básico.

use actix_web::{web, App, HttpResponse, HttpServer, Responder};

async fn status() -> impl Responder {
    HttpResponse::Ok().json({ "status": "Microservicio en ejecución" })
}

#[Actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .route("/status", web::get().to(status))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

Manejo de rutas con web::route().Respuesta en formato JSON usando HttpResponse::Ok().json().Ejecución de servidor en 127.0.0.1:8080.

Ejecutamos el servidor con:

cargo run

Probamos la API con curl:

curl http://127.0.0.1:8080/status

📦 Conectando con postgresql usando sqlx #

Para almacenar datos, configuramos PostgreSQL y conectamos nuestra API con SQLx.

📌 1️⃣ Configurar la base de datos #

Dentro de PostgreSQL, creamos la base de datos:

CREATE DATABASE microservicio;
CREATE USER rustaceo WITH ENCRYPTED PASSWORD 'password';
GRANT ALL PRIVILEGES ON DATABASE microservicio TO rustaceo;

En .env definimos la URL de conexión:

DATABASE_URL=postgres://rustaceo:password@localhost/microservicio

📌 2️⃣ Integrar sqlx en actix-web #

Modificamos main.rs para manejar la conexión a la base de datos:

use sqlx::PgPool;
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use dotenv::dotenv;
use std::env;

async fn obtener_datos(pool: web::Data<PgPool>) -> impl Responder {
    let datos = sqlx::query!("SELECT * FROM datos")
        .fetch_all(pool.get_ref())
        .await;
    
    match datos {
        Ok(datos) => HttpResponse::Ok().json(datos),
        Err(_) => HttpResponse::InternalServerError().finish(),
    }
}

#[Actix_web::main]
async fn main() -> std::io::Result<()> {
    dotenv().ok();
    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL no está configurado");
    let pool = PgPool::connect(&database_url).await.expect("No se pudo conectar a la BD");

    HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(pool.clone()))
            .route("/datos", web::get().to(obtener_datos))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

Carga de variables de entorno con dotenv().Conexión con PostgreSQL usando PgPool::connect().Consulta segura de datos con sqlx::query!().


📡 Comunicación entre microservicios #

Para que nuestros microservicios interactúen entre sí, usamos reqwest para realizar peticiones HTTP a otros servicios.

📌 Ejemplo de Consumo de API desde otro Microservicio:

use reqwest;

#[Tokio::main]
async fn main() {
    let respuesta = reqwest::get("http://127.0.0.1:8080/status")
        .await.unwrap()
        .text()
        .await.unwrap();
    
    println!("Respuesta del servicio: {}", respuesta);
}

Consumo de APIs con reqwest::get().Manejo de respuestas asíncronas con await.


🔐 Implementando seguridad con jwt #

Para proteger nuestras APIs, utilizamos autenticación con JSON Web Tokens (JWT):

📌 Ejemplo de Validación de JWT:

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

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

Protección contra accesos no autorizados.Generación y validación de tokens JWT.


🏆 Conclusión #

Hemos creado un microservicio en Rust desde cero con Actix-Web y SQLx.

🎯 Resumen de lo aprendido: ✔ Configuración de un servidor HTTP con Actix-Web. ✔ Conexión con PostgreSQL usando SQLx. ✔ Comunicación entre microservicios con reqwest. ✔ Implementación de autenticación con JWT.

🔮 Próximo paso: Desplegar el microservicio en Docker y Kubernetes.

¡Nos vemos en la siguiente entrega, Rustaceos!