Overview
Esta guía demuestra cómo compilar una aplicación web usando Actix Web y MongoDB. Actix es un potente framework web asíncrono para Rust que facilita la creación de servicios HTTP rápidos y seguros en cuanto a tipos.
La aplicación en este tutorial consta de las siguientes capas:
Capa de base de datos: MongoDB (alojada en MongoDB Atlas) almacena y recupera datos.
Capa de servidor: Actix Web proporciona el servidor HTTP, enrutamiento y endpoints API para conectar tu frontend a tu base de datos MongoDB.
Capa de gestión de datos: El sistema de tipos de Rust y asíncrono/await ofrecen un manejo seguro de datos con garantías de verificación en tiempo de compilación.
Capa de presentación: Páginas HTML renderizadas en el servidor, estilizadas con Tailwind CSS, que muestran los datos del restaurante de muestra en una tabla.
Compilará una pequeña aplicación que:
Se conecta a un clúster de MongoDB Atlas que contiene la
sample_restaurantsdatasetExpone un endpoint
/restaurantsque lista todos los restaurantesExpone un endpoint
/browseque lista restaurantes en Queens con"Moon"en su nombreRenderiza los resultados como una tabla HTML con una barra de navegación compartida.
¿Por qué usar MongoDB en una aplicación Actix?
El flexible modelo orientado a documentos de MongoDB almacena datos como documentos similares a BSON/JSON. Esto funciona perfectamente con structs Rust que modelan tus datos, sin capas ORM complejas ni migraciones de esquemas.
Cuando combinas Actix Web y el driver asíncrono MongoDB Rust, esta pila proporciona:
Estructuras de datos flexibles que pueden evolucionar sin migraciones costosas
E/S asíncrona y no bloqueante para altas concurrencias y APIs de alto rendimiento
Seguridad de tipos fuerte desde la capa de base de datos hasta tus manejadores y modelos
Integración sencilla con vistas web (plantillas HTML o creación manual de string, como en este tutorial)
Esta combinación funciona para aplicaciones que requieren lo siguiente:
La evolución de los esquemas a lo largo del tiempo
Alto rendimiento y concurrencia
Fuertes garantías en tiempo de compilación sobre las formas de datos
Tutorial de inicio rápido
Este tutorial te guía para desarrollar una aplicación Actix Web que se integra con MongoDB. La aplicación accede a datos de ejemplo de restaurantes en un clúster de MongoDB Atlas y muestra los resultados en tu navegador.
Tip
Si prefiere conectarse a MongoDB utilizando el controlador Rust sin Actix Web, consulte la Guía de Inicio rápido del controlador Rust.
Configurar el proyecto
Sigue los pasos de esta sección para instalar los requisitos previos, crear un clúster de MongoDB Atlas y preparar el proyecto Rust.
Verificar los prerrequisitos
Para crear la aplicación de Inicio rápido, asegúrese de tener lo siguiente instalado:
Requisito previo | notas |
|---|---|
Rust | Instala la última versión estable de Rust. |
Editor de código | Este tutorial utiliza Visual Studio Code con la extensión Rust, pero puedes usar el editor que prefieras. |
terminal | Usa Terminal o una aplicación similar para macOS. Usa PowerShell para Windows. |
Crea un clúster de MongoDB Atlas
MongoDB Atlas es un servicio de base de datos en la nube completamente gestionado que aloja tus implementaciones de MongoDB. Si no tienes una implementación de MongoDB, crea un clúster de MongoDB de manera gratuita (no se requiere tarjeta de crédito) completando el tutorial Introducción a MongoDB. El tutorial de MongoDB Get Started también demuestra cómo cargar conjuntos de datos de muestra en tu clúster, incluida la base de datos sample_restaurants que utiliza este tutorial.
Importante
Asegúrese de tener el set de datos sample_restaurants cargado en el clúster antes de continuar. La ausencia del set de datos sample_restaurants da lugar a listas vacías de restaurantes en las páginas de muestra.
Para conectarte a tu clúster de MongoDB, utiliza un URI de conexión. Para aprender cómo recuperar tu URI de conexión, consulta la sección Agregar tu cadena de conexión del tutorial de inicio de MongoDB.
Tip
Guarda la URI de tu conexión en un lugar seguro. La añadirás a un archivo .env más adelante.
Configura las dependencias de tu proyecto
Abra Cargo.toml y reemplace la sección [dependencies] con lo siguiente:
[package] name = "actix_quickstart" version = "0.1.0" edition = "2024" [dependencies] actix-web = "4" actix-files = "0.6" actix-cors = "0.7" mongodb = "3.4.1" tokio = { version = "1", features = ["rt-multi-thread", "macros"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" dotenv = "0.15" futures = "0.3"
Instala las nuevas dependencias ejecutando el siguiente comando en tu terminal:
cargo build
Estas dependencias proporcionan:
actix-web: Servidor HTTP, enrutamiento y tipos de solicitud/respuesta.mongodb:Controlador MongoDB asíncrono oficial para Rust.tokio: Entorno de ejecución asíncrono utilizado por Actix Web y el controlador MongoDB.serde/serde_json: Serialización y deserialización de datos JSON y BSON.dotenvCarga variables de entorno desde un .env archivo para desarrollo local.futures: Utilidades para trabajar con flujos asíncronos (utilizados para cursores de MongoDB).
Configurar el Back-End
Una vez que hayas configurado el Proyecto, sigue los pasos de esta sección para configurar las variables de entorno, establecer la conexión con MongoDB, definir el modelo de datos e implementar los servicios de consulta de la base de datos.
Configura las variables de entorno
Crea un archivo .env en la raíz de tu proyecto para almacenar el URI de conexión de MongoDB.
Ejecuta el siguiente comando en el directorio raíz de tu proyecto:
touch .env
Abre .env y añade tu URI de conexión y el número de puerto:
MONGO_URI="<your-mongodb-connection-uri>" PORT=5050
Reemplace <your-mongodb-connection-uri> con el URI de conexión que guardó anteriormente.
Crear el módulo de conexión a la base de datos
Cree un nuevo archivo llamado db.rs en el directorio src para administrar la conexión MongoDB.
Ejecuta el siguiente comando en el directorio raíz de tu proyecto:
touch src/db.rs
Abre db.rs y añade el siguiente código para configurar el Cliente de MongoDB y la conexión a la base de datos:
use mongodb::{options::ClientOptions, Client, Database}; pub async fn init_db(mongo_uri: &str) -> Database { let mut client_options = ClientOptions::parse(mongo_uri) .await .expect("Failed to parse MongoDB connection string"); client_options.app_name = Some("actix_quickstart".to_string()); let client = Client::with_options(client_options) .expect("Failed to initialize MongoDB client"); client.database("sample_restaurants") }
Este módulo:
Analiza la cadena de conexión de MongoDB desde tu entorno.
Configura el nombre de la aplicación del controlador para facilitar su observación.
Crea un Cliente y devuelve un identificador de base de datos para la base de datos
sample_restaurants.
Define el modelo de datos del restaurante
Creá un archivo nuevo llamado models.rs en el directorio src para definir el modelo de datos del restaurante.
Ejecuta el siguiente comando en el directorio raíz de tu proyecto:
touch src/models.rs
Abra models.rs y agregue el siguiente código para definir la estructura Restaurante:
use mongodb::bson::oid::ObjectId; use serde::{Deserialize, Serialize}; pub struct RestaurantRow{ pub name: Option<String>, pub borough: Option<String>, pub cuisine: Option<String>, pub id: Option<ObjectId>, }
Esta estructura representa un documento de restaurante en la colección de MongoDB. Utiliza anotaciones Serde para mapear campos de BSON a campos de estructuras Rust.
Crear el servicio de consulta de restaurantes
Cree un módulo de servicio que aísle la lógica de consulta de MongoDB de los controladores HTTP.
Ejecuta los siguientes comandos en la raíz de tu proyecto:
mkdir -p src/services touch src/services/restaurant_queries.rs
Abre restaurant_queries.rs y añade el siguiente código:
use futures::stream::TryStreamExt; use mongodb::{ bson::doc, Collection, }; use crate::models::RestaurantRow; pub async fn fetch_all(restaurants: &Collection<RestaurantRow>) -> Result<Vec<RestaurantRow>, mongodb::error::Error> { let cursor = restaurants .find(doc! {}) .projection(doc! { "name": 1, "borough": 1, "cuisine": 1, "_id": 1, }) .await?; cursor.try_collect().await } pub async fn fetch_by_borough( restaurants: &Collection<RestaurantRow>, borough: &str, name: &str, ) -> Result<Vec<RestaurantRow>, mongodb::error::Error> { let filter = doc! { "borough": borough, "name": { "$regex": name, "$options": "i" } }; let cursor = restaurants .find(filter) .projection(doc! { "name": 1, "borough": 1, "cuisine": 1, "_id": 1, }) .await?; cursor.try_collect().await }
Este archivo contiene dos funciones asincrónicas:
fetch_all()Devuelve todos los restaurantes con una proyección de campo.fetch_by_borough(): Devuelve los restaurantes filtrados por borough y por una regex insensible a mayúsculas y minúsculas en el nombre.
Ambas funciones devuelven Vec<RestaurantRow>, por lo que su capa de presentación no tiene que tratar con BSON sin procesar.
Configurar el front-end
Ahora que las capas de base de datos y de servicio están en su lugar, configure Actix Web para:
Inicialice la conexión de MongoDB
Definir el estado compartido de la aplicación
Exponer los puntos finales
/restaurantsy/browseRenderizar páginas HTML con Tailwind CSS
Crea los handlers de rutas HTTP
Crea un módulo que contenga tus manejadores de rutas HTTP de Actix Web y la lógica de renderización HTML.
Desde la raíz de tu proyecto, ejecuta el siguiente comando:
touch src/pages.rs
Abre pages.rs y añade el siguiente código:
use actix_web::{get, web, HttpResponse, Responder}; use crate::{ AppState, models::RestaurantRow, services::restaurant_queries, }; // Test endpoint to fetch all restaurants async fn get_restaurants(data: web::Data<AppState>) -> impl Responder { let result = restaurant_queries::fetch_all(&data.restaurants).await; match result { Ok(rows) => { let html = render_table_page("All Restaurants", &rows); HttpResponse::Ok() .content_type("text/html; charset=utf-8") .body(html) } Err(e) => HttpResponse::InternalServerError().body(format!("DB error: {e}")), } } // Endpoint to fetch restaurants by borough. Queens is used as an example. async fn get_restaurants_by_borough(data: web::Data<AppState>) -> impl Responder { let borough = "Queens"; // For demonstration, we use a fixed borough. This could be made dynamic. let name = "Moon"; // For demonstration, we use a fixed name filter. This could be made dynamic. let result = restaurant_queries::fetch_by_borough(&data.restaurants, borough, name).await; match result { Ok(rows) => { let title = format!(r#"{} Restaurants with "{}" in the Name"#, borough, name); let html = render_table_page(&title, &rows); HttpResponse::Ok() .content_type("text/html; charset=utf-8") .body(html) } Err(e) => HttpResponse::InternalServerError().body(format!("DB error: {e}")), } } // HTML Renderer fn render_table_page(title: &str, rows: &[RestaurantRow]) -> String { let mut html = String::new(); html.push_str(r#" <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://cdn.tailwindcss.com"></script> <title>"#); html.push_str(title); html.push_str(r#"</title> </head> <body class="w-full"> "#); // Navigation Bar html.push_str(r#" <nav class="bg-white px-6 py-2 shadow-md w-full"> <div class="flex justify-between items-center"> <a href="/restaurants"> <img alt="MongoDB Logo" class="h-10 inline" src="https://d3cy9zhslanhfa.cloudfront.net/media/3800C044-6298-4575-A05D5C6B7623EE37/4B45D0EC-3482-4759-82DA37D8EA07D229/webimage-8A27671A-8A53-45DC-89D7BF8537F15A0D.png" /> </a> <a href="/browse" class="text-lime-800 text-lg font-semibold hover:text-lime-700"> Browse </a> </div> </nav> "#); // Page Title html.push_str(r#"<h2 class="text-lg font-semibold px-6 py-4">"#); html.push_str(title); html.push_str("</h2>"); // Table Wrapper html.push_str( r#"<div class="border border-gray-200 shadow-md rounded-lg overflow-hidden mx-6 mb-6"> <div class="relative w-full overflow-auto"> <table class="w-full caption-bottom text-sm"> <thead class="[&_tr]:border-b bg-gray-50"> <tr class="border-b transition-colors hover:bg-muted/50"> <th class="px-4 py-3 text-left text-sm font-bold text-gray-700 w-1/3"> Name </th> <th class="px-4 py-3 text-left text-sm font-bold text-gray-700 w-1/3"> Borough </th> <th class="px-4 py-3 text-left text-sm font-bold text-gray-700 w-1/3"> Cuisine </th> </tr> </thead> <tbody class="[&_tr:last_child]:border-0"> "#, ); // Table Rows for row in rows { html.push_str(r#"<tr class="border-b transition-colors hover:bg-gray-50">"#); html.push_str(r#"<td class="p-4 align-middle">"#); html.push_str(row.name.as_deref().unwrap_or("")); html.push_str("</td>"); html.push_str(r#"<td class="p-4 align-middle">"#); html.push_str(row.borough.as_deref().unwrap_or("")); html.push_str("</td>"); html.push_str(r#"<td class="p-4 align-middle">"#); html.push_str(row.cuisine.as_deref().unwrap_or("")); html.push_str("</td>"); html.push_str("</tr>"); } // Closing tags html.push_str(r#" </tbody> </table> </div> </div> "#); html.push_str("</body></html>"); html }
Este módulo:
Define los puntos finales
/restaurantsy/browse.Llama a los servicios de consultas de base de datos
fetch_allyfetch_by_borough.Renderiza una página HTML completa con Tailwind CSS y una barra de navegación reutilizable.
Actualizar el archivo principal de la aplicación
Reemplace el contenido de main.rs con el siguiente código:
mod db; mod models; mod services; mod pages; use actix_web::{get, web, App, HttpResponse, HttpServer, Responder}; use dotenv::dotenv; use mongodb::bson::doc; use mongodb::Collection; use std::env; use crate::models::RestaurantRow; // Shared state to hold the MongoDB collection #[derive(Clone)] struct AppState { restaurants: Collection<RestaurantRow>, } #[get("/health")] async fn health_check() -> impl Responder { HttpResponse::Ok().body("Healthy") } #[actix_web::main] async fn main() -> std::io::Result<()> { dotenv().ok(); let mongo_uri = env::var("MONGO_URI").expect("MONGO_URI must be set in .env file"); let port: u16 = env::var("PORT") .unwrap_or_else(|_| "5050".to_string()) .parse() .expect("PORT must be a valid u16 number"); print!("Starting server on port {port}...\n"); let db = db::init_db(&mongo_uri).await; let restaurants: Collection<RestaurantRow> = db.collection::<RestaurantRow>("restaurants"); // Extra ping to be sure connection is working let ping_result = db.run_command(doc! {"ping": 1},).await; print!("MongoDB ping result: {ping_result:?}\n"); let state = AppState {restaurants}; HttpServer::new(move || { App::new() .app_data(web::Data::new(state.clone())) .service(health_check) .service(pages::get_restaurants) .service(pages::get_restaurants_by_borough) }) .bind(("127.0.0.1", port))? .run() .await }
Este archivo:
Declara los módulos utilizados en la aplicación (
db,models,services,pages).Define una estructura
AppStateque contiene la colección de restaurantes, compartida entre los controladores.Implementa el punto final
/health.Lee
MONGO_URIyPORTdesde el entorno.Inicializa la base de datos MongoDB y la colección
restaurants.Envía un ping a tu clúster de MongoDB para verificar la conectividad.
Inicia un servidor HTTP Actix Web y registra tus rutas:
/health/restaurants/browse
Verifica tu estructura de archivos
Antes de ejecutar la aplicación, asegúrate de que tu árbol de archivos esté estructurado de manera similar a la siguiente:
actix-quickstart ├── Cargo.toml <-- Project config + dependencies> ├── .env <-- Environment variables> └── src ├── main.rs <-- Application entry point> ├── db.rs <-- MongoDB connection module> ├── models.rs <-- RestaurantRow model + BSON mapping> ├── pages.rs <-- HTTP route handlers + HTML rendering> └── services ├── mod.rs <-- Service module exports> └── restaurant_queries.rs <-- MongoDB query services>
La herramienta de Rust crea archivos adicionales como target/ cuando se compila. Puedes ignorar estos archivos de forma segura.
Ejecutar la aplicación
Siga los pasos restantes para iniciar el servidor y visualizar los datos renderizados del restaurante.
Inicia el servidor Actix Web
Desde la raíz de tu proyecto, ejecuta el siguiente comando para iniciar el servidor:
cargo run
Cargo compila tu aplicación e inicia el servidor Actix Web en el puerto 5050 definido en tu archivo .env.
Cuando sea exitoso, verás una salida similar a la siguiente:
Starting server on port 5050... MongoDB ping result: Ok(Document({"ok": Int32(1)}))
¡Felicidades por completar el tutorial de inicio rápido!
Después de completar estos pasos, tendrás una aplicación Actix Web que se conecta a tu implementación de MongoDB, ejecuta queries sobre datos de restaurantes de muestra y renderiza los resultados.
Recursos adicionales
Para obtener más información sobre Actix Web, MongoDB y las herramientas relacionadas, consulta:
Actix Web documentación
Controlador Rust de MongoDB documentación

