44 - Construyendo una api rest en Rust - parte 1 diseño e implementación con actix-web

44 - Construyendo una api rest en Rust - parte 1 diseño e implementación con actix-web

¡Bienvenidos de nuevo a Rustaceo.es! En esta serie, construiremos una API REST en Rust desde cero, utilizando Actix-Web para manejar solicitudes HTTP de manera eficiente. Esta primera parte se enfocará en el diseño inicial de la API, la creación de rutas y la estructura básica sin almacenamiento en base de datos.


🚀 Objetivos de la api #

Nuestra API tendrá las siguientes características iniciales:

  1. Manejo de Pokémon: Permitirá registrar, listar y obtener información sobre Pokémon.
  2. Rutas y Endpoints básicos: Implementaremos rutas para operaciones CRUD básicas.
  3. Uso de Actix-Web: Un framework rápido y eficiente para Rust.

En esta primera fase, dejaremos de lado SQL o persistencia avanzada para centrarnos en la estructura básica de la API.


🛠 Retos a superar #

Antes de empezar, identifiquemos los principales desafíos que enfrentaremos: ✔ Definir una estructura clara para nuestros datos. ✔ Manejar peticiones HTTP y responder con datos JSON. ✔ Implementar una API escalable sin depender aún de una base de datos.


📂 Configuración del proyecto #

Empezamos creando un nuevo proyecto en Rust con Actix-Web:

cargo new rest-api-pokemon --bin
cd rest-api-pokemon

Ahora agregamos las dependencias en Cargo.toml:

[dependencies]
actix-web = "4"
serde = { version = "1", features = ["derive"] }
serde_json = "1"

Ejecutamos cargo check para asegurarnos de que todo está en orden.


🌐 Implementando el servidor con actix-web #

Definimos la estructura base de nuestro servidor y creamos un endpoint básico.

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
}

Ejecutamos el servidor con:

cargo run

Accedemos a http://127.0.0.1:8080/ y veremos nuestro mensaje de bienvenida.


🏗 Creando rutas para pokémon #

Ahora definimos nuestra estructura Pokémon y añadimos rutas para manejar Pokémon.

use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};
use std::sync::Mutex;

#[Derive(serialize, deserialize)]
struct Pokémon {
    id: usize,
    nombre: String,
    tipo: String,
}

struct AppState {
    elementoes: Mutex<Vec<Pokémon>>,
}

async fn listar_elemento(data: web::Data<AppState>) -> impl Responder {
    let elementoes = data.elementoes.lock().unwrap();
    HttpResponse::Ok().json(&*elementoes)
}

async fn agregar_elemento(data: web::Data<AppState>, nuevo_elemento: web::Json<Pokémon>) -> impl Responder {
    let mut elementoes = data.elementoes.lock().unwrap();
    elementoes.push(nuevo_elemento.into_inner());
    HttpResponse::Created().finish()
}

#[Actix_web::main]
async fn main() -> std::io::Result<()> {
    let data = web::Data::new(AppState {
        elementoes: Mutex::new(vec![]),
    });

    HttpServer::new(move || {
        App::new()
            .app_data(data.clone())
            .route("/elementoes", web::get().to(listar_elemento))
            .route("/elementoes", web::post().to(agregar_elemento))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

/elementoes (GET): Lista todos los Pokémon. ✔ /elementoes (POST): Agrega un nuevo Pokémon. ✔ Datos almacenados temporalmente en memoria con Mutex<Vec<Pokémon>>.


📌 Conclusión #

Hemos creado la primera versión de nuestra API REST en Rust con Actix-Web.

🎯 Resumen de lo aprendido: ✔ Configuración de un servidor con Actix-Web. ✔ Creación de rutas y manejo de JSON con serde. ✔ Uso de Mutex para almacenamiento en memoria.

🔮 Próximo paso: En la segunda parte, implementaremos persistencia con SQLx para almacenar Pokémon en una base de datos.

¡Nos vemos en la siguiente entrega, Rustaceos!