Visão geral
Neste guia, você pode aprender a criar um aplicação web Rust que utiliza oRocket como estrutura web. Foguete é uma estrutura para Rust que permite criar aplicativos da web seguros e com segurança de tipo.
O aplicação neste tutorial consiste nas seguintes camadas:
Camada de banco de dados: o MongoDB fornece armazenamento e recuperação de dados.
Camada de aplicativo: o Rock lida com solicitações HTTP, roteamento e processamento lógico.
Camada de apresentação: os modelos HTML renderizam dados de restaurantes em páginas da web.
Por que usar MongoDB com Rust e Foguete?
Ao integrar o MongoDB com o Rust e o Foguete, você pode usar as características de segurança e desempenho da memória do Rust junto com o modelo de documento flexível do MongoDB. O sistema de tipos do Vector oferece garantias de tempo de compilação para os endpoints da API, e o armazenamento de documento do MongoDB funciona eficientemente com as bibliotecas de serialização do Rust, como o Serde.
A combinação de Rust, Foguete e MongoDB oferece suporte a aplicativos que exigem alto desempenho, segurança de tipo e escalabilidade. Como resultado, essa pilha é bem projetada para aplicativos do mundo real, como APIs de alta taxa de transferência, microsserviços ou sistemas que exigem garantias rigorosas de desempenho.
Tutorial de início rápido
Este tutorial mostra como construir um aplicação da web que utiliza Rust e Foguete. O aplicação acessa dados de restaurantes de amostra, consulta os dados e os exibe por meio de modelos HTML com estilo. O tutorial também inclui instruções para se conectar a um cluster MongoDB hospedado no MongoDB Atlas e acessar e exibir dados de seu banco de dados.
Dica
Se você preferir se conectar ao MongoDB usando o driver Rust sem Foguete, consulte o tutorialInício Rápido do Driver Rust.
Configurar seu projeto
Siga as etapas nesta seção para instalar as dependências do projeto , criar um cluster do Atlas e configurar a estrutura do aplicação .
Verifique os pré-requisitos
Para criar o aplicação Quick Start, instale o seguinte software em seu ambiente de desenvolvimento:
Pré-requisitos | Notas |
|---|---|
Instale a versão estável mais recente usando | |
Editor de código | Este tutorial usa o Visual Studio Code com a extensão Rust, mas você pode usar o editor de sua escolha. |
Aplicativo de terminal e shell | Para usuários do MacOS, use o Terminal ou um aplicativo semelhante. Para usuários do Windows, use o PowerShell. |
Criar um cluster MongoDB Atlas
O MongoDB Atlas é um serviço de banco de dados de nuvem totalmente gerenciado que hospeda suas implementações do MongoDB . Se você não tiver uma implementação do MongoDB , poderá criar um cluster do MongoDB gratuitamente (nenhum cartão de crédito necessário) concluindo o tutorial de Introdução ao MongoDB . O tutorial de Introdução ao MongoDB também demonstra como carregar conjuntos de dados de exemplo em seu cluster, incluindo o sample_restaurants banco de dados usado neste tutorial.
Para se conectar ao cluster MongoDB , você deve usar um URI de conexão. Para saber como recuperar seu URI de conexão, consulte a seção Adicionar sua string de conexão do tutorial de Introdução ao MongoDB .
Importante
Salve sua string de conexão em um local seguro.
Configurar suas dependências de projeto
Navegue até o arquivo Cargo.toml e substitua seu conteúdo pelo seguinte código:
[package] name = "rocket-quickstart" version = "0.1.0" edition = "2024" [dependencies] rocket = {version = "0.5.0", features = ["json"]} serde = "1.0.136" dotenv = "0.15.0" tokio = {version = "1.32.0", features = ["full"]} rocket_dyn_templates = { version = "0.1.0", features = ["handlebars"] } [dependencies.mongodb] version = "3.4.1"
Instale as dependências executando o seguinte comando a partir do seu diretório rocket-quickstart:
cargo build
Este comando instala as seguintes dependências:
Foguete, a estrutura web do projeto com suporte a JSON
Serde, para serialização de dados
Driver MongoDB Rust, para operações MongoDB
Tokio, o tempo de execução assíncrono
shell_dyn_templates, para renderização de modelo de guiador
Criar a estrutura do projeto
Para criar os diretórios e arquivos necessários para este aplicação, execute os seguintes comandos a partir do seu diretório rocket-quickstart:
mkdir -p src/models src/repository src/api templates static touch src/models/restaurant.rs src/repository/mongodb_repo.rs src/api/restaurant_api.rs touch src/models.rs src/repository.rs src/api.rs touch templates/index.hbs static/style.css
Esses comandos estruturam seu projeto para seguir o padrão de arquitetura modular do Rust, que agrupa arquivos em diferentes diretórios de acordo com sua finalidade. Após executar os comandos, você tem os seguintes diretórios:
src/models: contém estruturas de dados para modelar dados de restaurantesrc/repository: Contém lógica de acesso ao banco de dadossrc/api: contém manipuladores de endpoint HTTPtemplates: contém um arquivo de modelo do Handlebars para renderização de dadosstatic: contém um arquivo CSS para estilo
Os comandos também criam arquivos de declaração de módulo para cada diretório src/, que listam os módulos disponíveis em seus diretórios correspondentes e permitem acessá-los em todo o aplicação.
Configurar o back-end
Após configurar a estrutura e as dependências do projeto , siga as etapas desta seção para se conectar ao MongoDB e configurar seus modelos de dados.
Forneça seu URI de conexão
Defina a variável de ambiente MONGO_URI para seu URI de conexão executando o seguinte comando no diretório rocket-quickstart:
export MONGO_URI="<connection URI>"
Substitua <connection URI> o espaço reservado pelo URI de conexão que você salvou em uma etapa anterior.
Crie seus modelos de dados
Navegue até o arquivo src/models/restaurant.rs e cole o seguinte código:
use serde::{Deserialize, Serialize}; use mongodb::bson::oid::ObjectId; pub struct Restaurant { pub id: Option<ObjectId>, pub name: Option<String>, pub borough: Option<String>, pub cuisine: Option<String>, pub address: Option<Address>, } pub struct Address { pub building: Option<String>, pub street: Option<String>, pub zipcode: Option<String>, }
Este arquivo define os modelos Restaurant e Address, que representam os dados na collection de amostra do sample_restaurants.restaurants.
Para utilizar seus modelos em seus arquivos de projeto , você deve registrar o módulo restaurant, que corresponde ao arquivo restaurant.rs. Navegue até o arquivo src/models.rs e cole o seguinte código:
pub mod restaurant; pub use restaurant::*;
Acesse a coleção de amostras do MongoDB
Navegue até o arquivo src/repository/mongodb_repo.rs e cole o seguinte código:
use std::env; use mongodb::{ bson::{extjson::de::Error, doc}, Client, Collection }; use crate::models::Restaurant; pub struct MongoRepo { col: Collection<Restaurant>, } impl MongoRepo { pub async fn init() -> Self { dotenv::dotenv().ok(); let uri = match env::var("MONGO_URI") { Ok(v) => v.to_string(), Err(_) => format!("Error loading env variable"), }; let client = Client::with_uri_str(uri).await.unwrap(); let db = client.database("sample_restaurants"); let col: Collection<Restaurant> = db.collection("restaurants"); MongoRepo { col } } pub async fn get_all_restaurants(&self) -> Result<Vec<Restaurant>, Error> { let mut cursor = self .col .find(doc! {}) .await .map_err(|e| Error::DeserializationError { message: e.to_string() })?; let mut restaurants: Vec<Restaurant> = Vec::new(); while cursor.advance().await.map_err(|e| Error::DeserializationError { message: e.to_string() })? { match cursor.deserialize_current() { Ok(restaurant) => restaurants.push(restaurant), Err(e) => { // Skips documents that can't be deserialized and logs the error eprintln!("Warning: Skipping document due to deserialization error: {}", e); continue; } } } Ok(restaurants) } pub async fn get_filtered_restaurants(&self) -> Result<Vec<Restaurant>, Error> { let filter = doc! { "borough": "Queens", "name": { "$regex": "Moon", "$options": "i" } }; let mut cursor = self .col .find(filter) .await .map_err(|e| Error::DeserializationError { message: e.to_string() })?; let mut restaurants: Vec<Restaurant> = Vec::new(); while cursor.advance().await.map_err(|e| Error::DeserializationError { message: e.to_string() })? { match cursor.deserialize_current() { Ok(restaurant) => restaurants.push(restaurant), Err(e) => { // Skips documents that can't be deserialized and logs the error eprintln!("Warning: Skipping document due to deserialization error: {}", e); continue; } } } Ok(restaurants) } }
Este arquivo acessa a coleção restaurants no banco de dados sample_restaurants . Em seguida, define os seguintes métodos de query que recuperam documentos da collection:
get_all_restaurants(): Recupera todos os documentos na coleçãorestaurantsget_filtered_restaurants(): recupera documentos na collectionrestaurantsque têm um valorboroughde"Queens"e um valornamecontendo"Moon". O valor$optionsespecifica que esta querynamenão diferencia maiúsculas de minúsculas.
Este código também inclui lógica para ignorar documentos que não podem ser desserializados. Isto evita erros quando os documentos não contêm cada campo definido no modelo Restaurant.
Em seguida, registre o módulo em src/repository.rs:
pub mod mongodb_repo; pub use mongodb_repo::*;
Configurar o front-end
Depois de configurar a camada de dados, siga as etapas desta seção para criar manipuladores e modelos da API do Foguete para a interface do usuário.
Criar os manipuladores de API
Navegue até o arquivo src/api/restaurant_api.rs e cole o seguinte código:
use rocket::{State, serde::json::Json, get, http::Status}; use crate::{models::Restaurant, repository::MongoRepo}; pub async fn get_all_restaurants(db: &State<MongoRepo>) -> Result<Json<Vec<Restaurant>>, Status> { let restaurants = db.get_all_restaurants().await; match restaurants { Ok(restaurants) => Ok(Json(restaurants)), Err(_) => Err(Status::InternalServerError), } } pub async fn get_filtered_restaurants(db: &State<MongoRepo>) -> Result<Json<Vec<Restaurant>>, Status> { let restaurants = db.get_filtered_restaurants().await; match restaurants { Ok(restaurants) => Ok(Json(restaurants)), Err(_) => Err(Status::InternalServerError), } }
Este código cria os endpoints /restaurants e /restaurants/browse . Esses endpoints fornecem dados JSON de seus métodos de query que serão consumidos pelo código JavaScript do frontend para carregar e exibir dinamicamente as informações do restaurante.
Em seguida, registre o módulo API no src/api.rs:
pub mod restaurant_api; pub use restaurant_api::*;
Atualizar o arquivo principal do aplicação
Navegue até o arquivo src/main.rs e cole o seguinte código:
mod api; mod models; mod repository; extern crate rocket; use rocket::{get, fs::FileServer, response::content::RawHtml}; use rocket_dyn_templates::Template; use api::restaurant_api::{get_all_restaurants, get_filtered_restaurants}; use repository::MongoRepo; async fn index() -> RawHtml<String> { let template_content = std::fs::read_to_string("templates/index.hbs").unwrap_or_else(|_| { r#"<!DOCTYPE html> <html><head><title>Error</title></head> <body><h1>Template not found</h1></body></html>"#.to_string() }); RawHtml(template_content) } async fn browse() -> RawHtml<String> { let template_content = std::fs::read_to_string("templates/index.hbs").unwrap_or_else(|_| { r#"<!DOCTYPE html> <html><head><title>Error</title></head> <body><h1>Template not found</h1></body></html>"#.to_string() }); RawHtml(template_content) } async fn rocket() -> _ { let db = MongoRepo::init().await; rocket::build() .manage(db) .mount("/", routes![index, browse, get_all_restaurants, get_filtered_restaurants]) .mount("/static", FileServer::from("static/")) .attach(Template::fairing()) }
Este arquivo configura o servidor webRocket e configura as seguintes rotas:
/: Retorna o código HTML que formata a página do índice principal, que mostra todos os restaurantes/browse: retorna o código HTML que formata a página de restaurantes filtrada
Criar o modelo do Handlebars
Navegue até o arquivo templates/index.hbs e cole o seguinte código:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Restaurant Explorer</title> <link rel="stylesheet" href="/static/style.css"> </head> <body> <div class="container"> <header> <h1>Restaurant Directory</h1> </header> <div class="controls"> <a href="/" class="btn" id="all-btn">All Restaurants</a> <a href="/browse" class="btn" id="browse-btn">Browse Filtered</a> </div> <div id="restaurants-container"> <div class="loading">Loading restaurants...</div> </div> </div> <script> // Sets the active button based on the current path const path = window.location.pathname; if (path === '/browse') { document.getElementById('browse-btn').classList.add('active'); } else { document.getElementById('all-btn').classList.add('active'); } // Loads the restaurant documents async function loadRestaurants() { try { const apiEndpoint = path === '/browse' ? '/restaurants/browse' : '/restaurants'; const response = await fetch(apiEndpoint); const restaurants = await response.json(); displayRestaurants(restaurants); } catch (error) { document.getElementById('restaurants-container').innerHTML = '<div class="error">Error loading restaurants. Please try again later.</div>'; } } // Displays restaurants in a grid format function displayRestaurants(restaurants) { const container = document.getElementById('restaurants-container'); if (restaurants.length === 0) { container.innerHTML = '<div class="error">No restaurants found.</div>'; return; } const restaurantsHTML = restaurants.map(function(restaurant) { return '<div class="restaurant-card">' + '<div class="restaurant-name">' + (restaurant.name || 'Unknown Name') + '</div>' + (restaurant.cuisine ? '<div class="cuisine-tag">' + restaurant.cuisine + '</div>' : '') + (restaurant.borough ? '<div class="restaurant-info"><strong>Borough:</strong> ' + restaurant.borough + '</div>' : '') + (restaurant.restaurant_id ? '<div class="restaurant-info"><strong>ID:</strong> ' + restaurant.restaurant_id + '</div>' : '') + (restaurant.address ? '<div class="address">' + (restaurant.address.building ? restaurant.address.building + ' ' : '') + (restaurant.address.street ? restaurant.address.street : '') + (restaurant.address.zipcode ? ', ' + restaurant.address.zipcode : '') + '</div>' : '') + '</div>'; }).join(''); container.innerHTML = '<div class="restaurants-grid">' + restaurantsHTML + '</div>'; } loadRestaurants(); </script> </body> </html>
Este modelo do Handlebars usa JavaScript para criar uma interface web responsiva que carrega dinamicamente dados de restaurantes de seus endpoints API e exibe os restaurantes em um layout de grade interativo.
Add CSS styling
Adicione o seguinte código a static/style.css:
/* Modern, clean styling for the restaurant app */ * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; line-height: 1.6; color: #333; background: rgb(198, 240, 209); min-height: 100vh; } .container { max-width: 1200px; margin: 0 auto; padding: 20px; } header { text-align: center; margin-bottom: 40px; color: rgb(0, 0, 0); } h1 { font-size: 3rem; margin-bottom: 10px; } .subtitle { font-size: 1.2rem; opacity: 0.9; } .controls { display: flex; justify-content: center; gap: 20px; margin-bottom: 30px; } .btn { background: white; color: #0d8958; border: none; padding: 12px 24px; border-radius: 25px; font-weight: 600; text-decoration: none; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 15px rgba(0,0,0,0.2); } .btn:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0,0,0,0.3); } .btn.active { background: #0d8958; color: white; } .restaurants-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); gap: 20px; margin-top: 20px; } .restaurant-card { background: white; border-radius: 15px; padding: 20px; box-shadow: 0 8px 25px rgba(0,0,0,0.1); transition: transform 0.3s ease, box-shadow 0.3s ease; } .restaurant-card:hover { transform: translateY(-5px); box-shadow: 0 12px 35px rgba(0,0,0,0.15); } .restaurant-name { font-size: 1.4rem; font-weight: 700; color: #000000; margin-bottom: 10px; } .restaurant-info { margin-bottom: 8px; } .restaurant-info strong { color: #555; } .cuisine-tag { display: inline-block; background: #0d8958; color: white; padding: 4px 12px; border-radius: 15px; font-size: 0.85rem; font-weight: 600; margin-top: 10px; } .address { color: #666; font-style: italic; margin-top: 8px; } .loading { text-align: center; color: white; font-size: 1.2rem; margin: 40px 0; } .error { text-align: center; color: #ff6b6b; background: white; padding: 20px; border-radius: 10px; margin: 20px 0; } @media (max-width: 768px) { .container { padding: 10px; } h1 { font-size: 2rem; } .controls { flex-direction: column; align-items: center; } .restaurants-grid { grid-template-columns: 1fr; } }
Este arquivo CSS modela os dados do restaurante exibidos nas páginas da web.
Execute seu aplicativo
Por fim, siga as etapas desta seção para executar seu aplicação da web e explorar os dados do restaurante usando a interface do navegador.
Inicie o aplicação Foguete
Navegue até o diretório do projeto e execute o seguinte comando:
cargo run
Se for bem-sucedida, a saída do comando será semelhante ao exemplo a seguir:
Configured for debug. >> address: 127.0.0.1 >> port: 8000 ... Routes: >> (index) GET / >> (browse) GET /browse >> (get_all_restaurants) GET /restaurants >> (get_filtered_restaurants) GET /restaurants/browse >> (FileServer: static/) GET /static/<path..> [10] Fairings: >> Templating (ignite, liftoff, request) >> Shield (liftoff, response, singleton) Shield: >> X-Frame-Options: SAMEORIGIN >> Permissions-Policy: interest-cohort=() >> X-Content-Type-Options: nosniff Templating: >> directory: templates >> engines: ["hbs"] Rocket has launched from http://127.0.0.1:8000
Abra o aplicação da web
Abra http://:127.0.0.1 8000 em seu navegador da web. A página de destino inicial exibe todos os restaurantes na sample_restaurants.restaurants coleção:

Em seguida, clique no botão Browse Filtered para visualizar os restaurantes no Queins que têm "Moon" em seu nome:

Teste os endpoints da API
Você também pode testar diretamente os endpoints da API subjacentes executando os seguintes comandos no seu terminal:
curl http://127.0.0.1:8000/restaurants curl http://127.0.0.1:8000/restaurants/browse
Esses endpoints retornam os mesmos dados exibidos na interface da web, formatados como documentos JSON.
Parabéns por concluir o tutorial Início rápido! Depois de concluir essas etapas, você tem um aplicação web Rust and Rock que se conecta à implementação do MongoDB , executa queries em dados de restaurantes de exemplo e exibe os resultados em uma interface web hospedada localmente.
Recursos adicionais
Para saber mais sobre Rust, Foguete e MongoDB, consulte os seguintes recursos:
Documentaçãodo driver do MongoDB Rust
Estrutura de serialização doSerde