Join us at MongoDB.local London on 7 May to unlock new possibilities for your data. Use WEB50 to save 50%.
Register now >
Docs Menu
Docs Home
/ /

Aplicación web CRUD con Rocket

En este tutorial, puede aprender a crear una aplicación web Rust utilizando el Rocket framework web. El Rust driver te permite aprovechar funcionalidades como la gestión de memoria, las vidas de objetos y el pooling de bases de datos para mejorar el rendimiento de tu aplicación.

Después de completar este tutorial, tendrá una aplicación web con rutas para realizar operaciones CRUD.

Tip

Aplicación completa

Para ver una versión completa de la aplicación creada en este tutorial, visita el repositorio mongodb-api-rs en GitHub.

Asegúrese de tener Rust 1.74 o posterior y Cargo, el gestor de paquetes de Rust, instalados en su entorno de desarrollo.

Para obtener información sobre cómo instalar Rust y Cargo, consulta la guía oficial de Rust en descargar e instalar Rust.

También debe configurar un clúster de MongoDB Atlas. Para aprender a crear un clúster, consulta el Crear una implementación de MongoDB paso de la guía de inicio rápido. Guarda tu cadena de conexión en una ubicación segura para usarla más adelante en el tutorial.

1

Ejecute los siguientes comandos para crear y entrar en el rust-crud-app directorio del proyecto:

cargo new rust-crud-app
cd rust-crud-app
2

Ejecute el siguiente comando para añadir el controlador Rust:

cargo add mongodb

Verifica que tu archivo Cargo.toml contenga la siguiente entrada para el controlador Rust:

[dependencies]
mongodb = "3.5.2"
3

Cuando se utiliza el controlador Rust, se debe seleccionar el entorno de ejecución sincrónico o asincrónico. Este tutorial utiliza el entorno de ejecución asíncrona, que es más adecuado para construir APIs.

El driver se ejecuta con el entorno asíncrono tokio por defecto.

Para obtener más información sobre los tiempos de ejecución disponibles, consulte la guía de API asincrónicas y sincrónicas.

4

Accede a la interfaz de usuario de Atlas y luego selecciona la Collections Pestaña en la configuración del clúster. Seleccione el botón + Create Database. En la ventana modal, cree una base de datos llamada bread y, dentro de ella, una colección llamada recipes.

5

Selecciona el botón INSERT DOCUMENT y pega el contenido del archivo bread_data.json en el repositorio de la aplicación de muestra.

Después de insertar los datos, puedes ver los documentos de ejemplo en la colección recipes.

6

Abre tu IDE y entra en tu directorio de proyecto. Ejecuta el siguiente comando desde la raíz del proyecto para instalar el framework web Rocket:

cargo add -F json rocket

Verifica que la lista de dependencias en tu archivo Cargo.toml contenga una entrada para rocket.

También debe agregar un contenedor desarrollado por Rocket que le permite usar un contenedor para administrar un grupo de colecciones para las conexiones asíncronas realizadas por su cliente MongoDB. Este contenedor le permite parametrizar sus bases de datos y colecciones MongoDB y que cada función de la aplicación reciba su propia conexión.

Ejecuta el siguiente comando para agregar la caja rocket_db_pools:

cargo add -F mongodb rocket_db_pools

Verifique que la lista de dependencias en su archivo Cargo.toml contenga una entrada para rocket_db_pools que contenga un indicador de característica para mongodb.

7

Para configurar Rocket y usar la base de datos bread, crea un archivo llamado Rocket.toml en la raíz de tu proyecto. Rocket busca este archivo para leer la configuración. También puedes almacenar tu cadena de conexión de MongoDB en este archivo.

Pegue la siguiente configuración en Rocket.toml:

[default.databases.db]
url = "<connection string>"

Para obtener más información sobre la configuración de Rocket, consulta Configuración en la documentación de Rocket.

8

Antes de que empieces a escribir la API, aprende sobre la estructura de una aplicación Rocket simple y crea los archivos correspondientes en tu aplicación.

El siguiente diagrama demuestra la estructura de archivos que debe tener tu aplicación Rocket y explica la función de cada archivo:

.
├── Cargo.lock # Dependency info
├── Cargo.toml # Project and dependency info
├── Rocket.toml # Rocket configuration
└── src # Directory for all app code
├── db.rs # Establishes database connection
├── main.rs # Starts the web app
├── models.rs # Organizes data
└── routes.rs # Stores API routes

Cree el directorio src y los archivos que contiene, según el diagrama anterior. En este punto, los archivos pueden estar vacíos.

9

Pegue el siguiente código en el archivo db.rs.

use rocket_db_pools::{mongodb::Client, Database};
#[derive(Database)]
#[database("db")]
pub struct MainDatabase(Client);

También debes adjuntar la estructura de la base de datos a tu instancia de Rocket. En main.rs, inicialice la base de datos y adjúntela, como se muestra en el siguiente código:

mod db;
mod models;
mod routes;
use rocket::{launch, routes};
use rocket_db_pools::Database;
#[launch]
fn rocket() -> _ {
rocket::build()
.attach(db::MainDatabase::init())
.mount()
}

Tu IDE podría mostrar un error indicando que faltan argumentos en mount(). Puede ignorar este error ya que añadirá rutas en un paso posterior.

10

Definir structs coherentes y útiles para representar tus datos es importante para mantener la seguridad de tipos y reducir errores en tiempo de ejecución.

En tu archivo models.rs, define una estructura Recipe que represente una receta para hornear pan.

use mongodb::bson::oid::ObjectId;
use rocket::serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct Recipe {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
pub id: Option<ObjectId>,
pub title: String,
pub ingredients: Vec<String>,
pub temperature: u32,
pub bake_time: u32,
}
11

El enrutamiento permite que el programa dirija la solicitud al punto final adecuado para enviar o recibir los datos. El archivo routes.rs almacena todas las rutas definidas en la API.

Agregue el siguiente código a su archivo routes.rs para definir la ruta de índice y una ruta simple get_recipes():

use crate::db::MainDatabase;
use crate::models::Recipe;
use mongodb::bson::doc;
use rocket::{futures::TryStreamExt, get, serde::json::Json};
use rocket_db_pools::{mongodb::Cursor, Connection};
#[get("/")]
pub fn index() -> Json<Value> {
Json(json!({"status": "It is time to make some bread!"}))
}
#[get("/recipes", format = "json")]
pub async fn get_recipes(db: Connection<MainDatabase>) -> Json<Vec<Recipe>> {
let recipes: Cursor<Recipe> = db
.database("bread")
.collection("recipes")
.find(None, None)
.await
.expect("Failed to retrieve recipes");
Json(recipes.try_collect().await.unwrap())
}

Antes de guardar las rutas restantes, añade las rutas a la función principal de lanzamiento de Rocket.

En main.rs, reemplaza los argumentos en mount() para que el archivo se asemeje al siguiente código:

mod db;
mod models;
mod routes;
use rocket::{launch, routes};
use rocket_db_pools::Database;
#[launch]
fn rocket() -> _ {
rocket::build().attach(db::MainDatabase::init()).mount(
"/",
routes![
routes::index,
routes::get_recipes,
routes::create_recipe,
routes::update_recipe,
routes::delete_recipe,
routes::get_recipe
],
)
}
12

En tu aplicación, debes implementar el manejo de errores y respuestas personalizadas para abordar resultados inesperados de tus operaciones CRUD.

Instale el paquete serde_json ejecutando el siguiente comando:

cargo add serde_json

Esta caja incluye el enum Value que representa un valor JSON.

Puede especificar que sus rutas devuelvan códigos de estado HTTP utilizando las estructuras status::Custom de Rocket, que le permiten especificar el código de estado HTTP y cualquier dato personalizado que se deba devolver. El siguiente paso describe cómo escribir rutas que devuelvan el tipo status::Custom.

13

Al intentar crear datos en MongoDB, pueden darse dos resultados:

  • El documento se crea correctamente, por lo que la aplicación devuelve HTTP 201.

  • Se produjo un error durante la inserción, por lo que su aplicación devuelve HTTP 400.

Agregue la siguiente ruta a su archivo routes.rs para definir la ruta create_recipe() e implementar el manejo de errores:

#[post("/recipes", data = "<data>", format = "json")]
pub async fn create_recipe(
db: Connection<MainDatabase>,
data: Json<Recipe>,
) -> status::Custom<Json<Value>> {
if let Ok(res) = db
.database("bread")
.collection::<Recipe>("recipes")
.insert_one(data.into_inner(), None)
.await
{
if let Some(id) = res.inserted_id.as_object_id() {
return status::Custom(
Status::Created,
Json(json!({"status": "success", "message": format!("Recipe ({}) created successfully", id.to_string())})),
);
}
}
status::Custom(
Status::BadRequest,
Json(json!({"status": "error", "message":"Recipe could not be created"})),
)
}

Cuando intenta leer datos de MongoDB, hay dos resultados posibles:

  • Devuelve el vector de documentos coincidentes.

  • Devuelve un vector vacío porque no hay documentos coincidentes o porque se ha producido un error.

Debido a estos resultados esperados, reemplace su ruta get_recipes() con el siguiente código:

#[get("/recipes", format = "json")]
pub async fn get_recipes(db: Connection<MainDatabase>) -> Json<Vec<Recipe>> {
let recipes = db
.database("bread")
.collection("recipes")
.find(None, None)
.await;
if let Ok(r) = recipes {
if let Ok(collected) = r.try_collect::<Vec<Recipe>>().await {
return Json(collected);
}
}
return Json(vec![]);
}

Puedes copiar las rutas get_recipe(), update_recipe() y delete_recipe() del archivo routes.rs en el repositorio de la aplicación de ejemplo.

14

Inicie su aplicación ejecutando el siguiente comando en su terminal:

cargo run

En otra ventana de terminal, ejecute el siguiente comando para probar la ruta create_recipe():

curl -v --header "Content-Type: application/json" --request POST --data '{"title":"simple bread recipe","ingredients":["water, flour"], "temperature": 250, "bake_time": 120}' http://127.0.0.1:8000/recipes
{"status":"success","message":"Recipe (684c4245f5a3ca09efa92593) created successfully"}

Ejecuta el siguiente comando para probar la ruta get_recipes():

curl -v --header "Content-Type: application/json" --header "Accept: application/json" http://127.0.0.1:8000/recipes/
[{"_id":...,"title":"artisan","ingredients":["salt","flour","water","yeast"],"temperature":404,"bake_time":5},
{"_id":...,"title":"rye","ingredients":["salt"],"temperature":481,"bake_time":28},...]

Ejecuta el siguiente comando para probar la ruta delete_recipe(). Sustituya el marcador de posición <id> con un valor _id conocido de su colección, que podría parecerse a 68484d020f561e78c03c7800:

curl -v --header "Content-Type: application/json" --header "Accept: application/json" --request DELETE http://127.0.0.1:8000/recipes/<id>
{"status":"","message":"Recipe (68484d020f561e78c03c7800) successfully deleted"}

En este tutorial, aprendiste cómo compilar una aplicación web simple con Rocket para realizar operaciones CRUD.

Para obtener más información sobre las operaciones CRUD, consulte las siguientes guías:

Volver

Operaciones compuestas