47 - Creando una api restful en Rust - una guía completa

47 - Creando una api restful en Rust - una guía completa

¡Bienvenidos de nuevo a Rustaceo.es! En esta guía, haremos un recorrido por todo el proceso de construcción de una API RESTful en Rust, desde el diseño inicial hasta la integración con bases de datos y la implementación de pruebas automatizadas.

Si has seguido nuestras entregas anteriores, ahora consolidaremos todos esos conocimientos en una visión completa de cómo construir una API REST en Rust de manera eficiente y segura.


🚀 Pasos claves en la construcción de una api restful #

  1. Diseño e Implementación Base con Actix-Web.
  2. Integración con SQLx para la Persistencia de Datos.
  3. Pruebas Automatizadas para Asegurar la Calidad.

🏗 1️⃣ Implementación base con actix-web #

La base de cualquier API en Rust comienza con la configuración de un servidor HTTP. Actix-Web es un framework rápido y eficiente que nos permite manejar rutas, peticiones y respuestas con facilidad.

📌 Estructura Base de un Servidor en Actix-Web

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

async fn bienvenida() -> impl Responder {
    HttpResponse::Ok().body("¡Bienvenido a la API de Pokémon en Rust!")
}

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

Manejo de solicitudes HTTP con HttpServer y App.Definición de rutas con web::route().Respuesta básica en JSON.


📦 2️⃣ Persistencia con sqlx y postgresql #

El siguiente paso es agregar una base de datos para almacenar y gestionar datos. Para esto, utilizamos SQLx, una biblioteca asincrónica para trabajar con bases de datos en Rust.

📌 Conexión con PostgreSQL usando SQLx

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

#[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()))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

Configuración de conexión a PostgreSQL.Uso de dotenv para manejar variables de entorno.Reutilización del pool de conexiones con web::Data.

📌 Ejemplo de consulta a la base de datos

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

Ejecución de consultas seguras con SQLx.Manejo de errores para evitar caídas en la API.


🛠 3️⃣ Implementación de tests automatizados #

Para garantizar la calidad de nuestra API, implementamos tests unitarios y de integración utilizando actix-web::test.

📌 Ejemplo de Test para la Ruta de Bienvenida

#[Cfg(test)]
mod tests {
    use super::*;
    use actix_web::{test, App};

    #[actix_web::test]
    async fn test_bienvenida() {
        let app = test::init_service(App::new().route("/", web::get().to(bienvenida))).await;
        let req = test::TestRequest::get().uri("/").to_request();
        let resp = test::call_service(&app, req).await;
        assert!(resp.status().is_success());
    }
}

Pruebas automatizadas para garantizar respuestas correctas.Uso de test::init_service() para inicializar la API.

📌 Test para la Creación de un Pokémon

#[Actix_web::test]
async fn test_agregar_elemento() {
    let pool = establecer_conexion_test().await;
    let app = test::init_service(
        App::new()
            .app_data(web::Data::new(pool.clone()))
            .route("/elementoes", web::post().to(agregar_elemento)),
    )
    .await;

    let nuevo_elemento = json!({"nombre": "Item4", "tipo": "Planta"});
    let req = test::TestRequest::post()
        .uri("/elementoes")
        .set_json(&nuevo_elemento)
        .to_request();

    let resp = test::call_service(&app, req).await;
    assert_eq!(resp.status(), 201);
}

Verificación de la creación de un Pokémon en la base de datos.Uso de test::set_json() para simular una petición POST.


📌 Conclusión #

Hemos consolidado todos los elementos clave de nuestra API RESTful en Rust:

🎯 Resumen de lo aprendido: ✔ Configuración y estructura base con Actix-Web. ✔ Persistencia con PostgreSQL usando SQLx. ✔ Implementación de pruebas automatizadas.

🔮 Próximo paso: Mejorar la API con autenticación, paginación y control de acceso.

¡Nos vemos en la próxima entrega, Rustaceos!