Frontend Integration
Integrating Rust backends with frontend applications and full-stack Rust.
Advancedā±ļø 50 minš Prerequisites: 1
Frontend Integration
Rust backends can integrate with various frontend technologies, and Rust can also be used for frontend development.
Backend + Frontend Architecture
Traditional: Rust API + JavaScript Frontend
Frontend (React/Vue/Angular)
ā HTTP/REST
Rust Backend (Axum/Actix)
ā
Database
Full-Stack Rust
Frontend (Yew/Leptos/Dioxus)
ā HTTP/WebSocket
Rust Backend (Axum/Actix)
ā
Database
Rust Backend for Frontend
CORS Configuration
RUST// src/api/middleware/cors.rs use tower_http::cors::{CorsLayer, Any, AllowOrigin}; pub fn cors_layer() -> CorsLayer { CorsLayer::new() .allow_origin(Any) .allow_methods(Any) .allow_headers(Any) .allow_credentials(true) } // In main.rs let app = Router::new() .route("/api/users", get(get_users)) .layer(cors_layer());
API Structure for Frontend
RUST// src/api/routes.rs pub fn create_router() -> Router { Router::new() .nest("/api/v1", api_v1_routes()) .layer(cors_layer()) } fn api_v1_routes() -> Router { Router::new() .route("/users", get(get_users).post(create_user)) .route("/users/:id", get(get_user).put(update_user)) .route("/auth/login", post(login)) .route("/auth/register", post(register)) }
WebSocket Support
RUST// src/api/websocket.rs use axum::extract::ws::{WebSocket, Message}; pub async fn websocket_handler(socket: WebSocket) { let (mut sender, mut receiver) = socket.split(); while let Some(msg) = receiver.next().await { match msg { Ok(Message::Text(text)) => { // Handle message from frontend sender.send(Message::Text("Response".to_string())).await; } Ok(Message::Close(_)) => break, _ => {} } } } // In routes .route("/ws", get(websocket_handler))
Rust Frontend Frameworks
1. Yew (React-like)
TOML[dependencies] yew = "0.21" wasm-bindgen = "0.2"
RUST// src/lib.rs use yew::prelude::*; #[function_component] fn App() -> Html { let counter = use_state(|| 0); let increment = { let counter = counter.clone(); Callback::from(move |_| counter.set(*counter + 1)) }; html! { <div> <p>{ *counter }</p> <button onclick={increment}>{ "Increment" }</button> </div> } } #[wasm_bindgen::prelude::wasm_bindgen(start)] pub fn main() { yew::Renderer::<App>::new().render(); }
2. Leptos (Modern, Fast)
TOML[dependencies] leptos = "0.6" leptos_axum = "0.6"
RUST// src/lib.rs use leptos::*; #[component] fn App() -> impl IntoView { let (count, set_count) = create_signal(0); view! { <div> <p>{count}</p> <button on:click=move |_| set_count.update(|n| *n += 1)> "Increment" </button> </div> } }
3. Dioxus (Cross-platform)
TOML[dependencies] dioxus = "0.5"
RUST// src/main.rs use dioxus::prelude::*; fn main() { dioxus::launch(App); } fn App() -> Element { let mut count = use_signal(|| 0); rsx! { div { p { "Count: {count}" } button { onclick: move |_| count += 1, "Increment" } } } }
Full-Stack Rust Project Structure
fullstack-rust/
āāā Cargo.toml # Workspace
āāā backend/
ā āāā Cargo.toml
ā āāā src/
ā āāā main.rs
ā āāā api/
ā āāā services/
āāā frontend/
ā āāā Cargo.toml
ā āāā src/
ā āāā lib.rs
āāā shared/
āāā Cargo.toml # Shared types
āāā src/
āāā lib.rs
Shared Types
RUST// shared/src/lib.rs use serde::{Serialize, Deserialize}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct User { pub id: u64, pub email: String, pub name: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CreateUserRequest { pub email: String, pub name: String, }
Frontend-Backend Communication
RUST// frontend/src/api.rs use wasm_bindgen_futures::spawn_local; use gloo_net::http::Request; pub async fn fetch_users() -> Result<Vec<User>, Error> { let response = Request::get("http://localhost:3000/api/users") .send() .await?; response.json().await } // In component let users = use_state(|| Vec::new()); use_effect(move || { spawn_local(async move { if let Ok(fetched_users) = fetch_users().await { users.set(fetched_users); } }); });
Deployment Architecture
Development
Frontend (localhost:8080)
ā
Backend (localhost:3000)
ā
Database
Production
CDN/Static Hosting (Frontend)
ā HTTPS
Load Balancer
ā
Rust Backend (Multiple Instances)
ā
Database Cluster
Best Practices
- API versioning: Version your APIs
- Type safety: Share types between frontend/backend
- Error handling: Consistent error responses
- Authentication: JWT tokens, secure cookies
- CORS: Configure properly for production
- WebSocket: For real-time features
- SSR: Server-side rendering for SEO (Leptos supports this)
- Build optimization: Optimize WASM bundle size
Code Examples
Full-Stack Structure
Organizing full-stack Rust project
RUST
fn main() {
println!("Full-stack Rust:");
println!("- Backend: API server");
println!("- Frontend: WebAssembly");
println!("- Shared: Common types");
}Explanation:
Full-stack Rust projects use a workspace with separate crates for backend, frontend, and shared code. This enables type safety across the stack.
Frontend-Backend Integration
Frontend calling Rust backend
RUST
struct User {
id: u64,
name: String,
}
fn main() {
println!("Frontend-Backend integration:");
println!("- Frontend makes HTTP requests");
println!("- Backend returns JSON");
println!("- Shared types ensure type safety");
}Explanation:
Frontend and backend communicate via HTTP. Shared types ensure both sides use the same data structures, providing compile-time type safety.
Exercises
Shared Type
Create a shared User type that can be used by both frontend and backend!
Starter Code:
RUST
fn main() {
println!("Shared type created!");
}