Overview
Esta guía muestra cómo crear una aplicación web con 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 con seguridad de tipos.
La aplicación de 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 async/await proporcionan un manejo seguro de datos con garantías en tiempo de compilación.
Capa de presentación: las páginas HTML renderizadas por el servidor, diseñadas con Tailwind CSS, muestran los datos del restaurante de muestra en una tabla.
Construirás una pequeña aplicación que:
Se conecta a un clúster MongoDB Atlas que contiene el
sample_restaurantsdatasetExpone un endpoint
/restaurantsque lista todos los restaurantesExpone un punto final
/browseque enumera los restaurantes en Queens con"Moon"en el nombreRenderiza los resultados como una tabla HTML con una barra de navegación compartida.
¿Por qué utilizar MongoDB en una aplicación Actix?
El modelo de documentos flexible de MongoDB almacena datos como documentos tipo BSON/JSON. Esto funciona de forma natural con las estructuras de Rust que modelan tus datos, sin complejas capas ORM ni migraciones de esquemas.
Cuando combina Actix Web y el controlador asincrónico MongoDB Rust, esta pila proporciona:
Estructuras de datos flexibles que pueden evolucionar sin migraciones costosas
E/S asincrónica y sin bloqueo para API de alto rendimiento y alta concurrencia
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 cadenas, como en este tutorial)
Esta combinación funciona para aplicaciones que requieren lo siguiente:
Esquemas en evolución a lo largo del tiempo
Alto rendimiento y concurrencia
Fuertes garantías de 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íade inicio rápido del controlador Rust.
Configurar el proyecto
Siga los pasos de esta sección para instalar los requisitos previos, crear un clúster MongoDB Atlas y estructurar el proyecto Rust.
Verificar los prerrequisitos
Para crear la aplicación de inicio rápido, asegúrese de tener instalado lo siguiente:
Requisito previo | notas |
|---|---|
Rust | Instale 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. |
Crear un clúster de MongoDB Atlas
MongoDB Atlas es un servicio de base de datos en la nube totalmente administrado que aloja sus implementaciones de MongoDB. Si no tiene una implementación de MongoDB, cree un clúster de MongoDB gratis (no se requiere tarjeta de crédito) completando el tutorial de introducción a MongoDB. Este tutorial también muestra cómo cargar conjuntos de datos de muestra en su clúster, incluyendo la sample_restaurants base de datos que utiliza este tutorial.
Importante
Asegúrese de tener el conjunto de datos sample_restaurants cargado en su clúster antes de continuar. Si falta el conjunto de datos sample_restaurants, las listas de restaurantes en sus páginas de muestra estarán vacías.
Para conectarse a su clúster de MongoDB, use una URI de conexión. Para saber cómo recuperarla, consulte la sección "Añadir la cadena de conexión" del tutorial "Introducción a MongoDB".
Tip
Guarda la URI de tu conexión en un lugar seguro. La añadirás a un archivo .env más adelante.
Configurar las dependencias de su 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"
Instale las nuevas dependencias ejecutando el siguiente comando en su 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:Tiempo 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.dotenv:Carga variables de entorno desde un archivo .env para desarrollo local.futures:Utilidades para trabajar con transmisiones asíncronas (utilizadas para cursores MongoDB).
Configurar el back end
Después de configurar el proyecto, siga los pasos de esta sección para configurar las variables de entorno, configurar su conexión MongoDB, definir su modelo de datos e implementar los servicios de consulta de base de datos.
Configurar sus variables de entorno
Cree un archivo .env en la raíz de su proyecto para almacenar su URI de conexión MongoDB.
Ejecute el siguiente comando en la raíz de su proyecto:
touch .env
Abra .env y agregue su URI de conexión y número de puerto:
MONGO_URI="<your-mongodb-connection-uri>" PORT=5050
Reemplace <your-mongodb-connection-uri> con la 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.
Ejecute el siguiente comando en la raíz de su proyecto:
touch src/db.rs
Abra db.rs y agregue el siguiente código para configurar la conexión de la base de datos y el cliente MongoDB:
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 de su 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.
Definir el modelo de datos del restaurante
Cree un nuevo archivo llamado models.rs en el directorio src para definir el modelo de datos del restaurante.
Ejecute el siguiente comando en la raíz de su 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 MongoDB. Utiliza anotaciones Serde para asignar campos BSON a campos de estructura 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.
Ejecute los siguientes comandos en la raíz de su 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 restaurantes filtrados por distrito y por una expresión regular de nombre que no distingue entre mayúsculas y minúsculas.
Ambas funciones devuelven Vec<RestaurantRow>, por lo que su capa de presentación no necesita lidiar 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 de la aplicación compartida
Exponer los puntos finales
/restaurantsy/browseRenderizar páginas HTML con Tailwind CSS
Crea los handlers de rutas HTTP
Cree un módulo que contenga sus controladores de ruta HTTP de Actix Web y la lógica de representación HTML.
Desde la raíz de su proyecto, ejecute 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 consulta de base de datos
fetch_allyfetch_by_borough.Representa 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_URIyPORTdel entorno.Inicializa la base de datos MongoDB y la colección
restaurants.Hace ping a su clúster MongoDB para verificar la conectividad.
Inicia un servidor HTTP web Actix y registra sus rutas:
/health/restaurants/browse
Verifique la estructura de su archivo
Antes de ejecutar la aplicación, asegúrese de que su á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>
Las herramientas de Rust crean archivos adicionales, como target/, durante la compilación. Puedes ignorarlos sin problema.
Ejecute su aplicación
Siga los pasos restantes para iniciar el servidor y ver los datos del restaurante procesados.
Inicie el servidor web Actix
Desde la raíz de su proyecto, ejecute el siguiente comando para iniciar el servidor:
cargo run
Cargo compila su aplicación e inicia el servidor web Actix en el puerto 5050 definido en su archivo .env.
Cuando tenga éxito, verá un resultado similar al siguiente:
Starting server on port 5050... MongoDB ping result: Ok(Document({"ok": Int32(1)}))
¡Felicitaciones por completar el tutorial de inicio rápido!
Después de completar estos pasos, tendrá una aplicación web Actix que se conecta a su implementación de MongoDB, ejecuta consultas en datos de restaurantes de muestra y presenta los resultados.
Recursos adicionales
Para obtener más información sobre Actix Web, MongoDB y herramientas relacionadas, consulte:
Documentaciónweb de Actix
Documentación delcontrolador Rust de MongoDB

