REST API Architecture
Building RESTful APIs with Rust using modern frameworks.
Intermediateā±ļø 50 minš Prerequisites: 1
REST API Architecture
Building production-ready REST APIs with Rust requires proper structure and framework selection.
Popular Rust Web Frameworks
1. Axum (Recommended)
Modern, async-first framework built on Tokio
TOML[dependencies] axum = "0.7" tokio = { version = "1.0", features = ["full"] } tower = "0.4" tower-http = { version = "0.5", features = ["cors"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0"
2. Actix Web
High-performance, actor-based framework
TOML[dependencies] actix-web = "4.4" actix-rt = "2.9" serde = { version = "1.0", features = ["derive"] }
3. Rocket
Easy-to-use, type-safe framework
TOML[dependencies] rocket = { version = "0.5", features = ["json"] } serde = { version = "1.0", features = ["derive"] }
Axum API Structure
src/
āāā main.rs
āāā lib.rs
āāā api/
ā āāā mod.rs
ā āāā routes.rs # Route definitions
ā āāā handlers/ # Request handlers
ā ā āāā mod.rs
ā ā āāā user_handler.rs
ā ā āāā transaction_handler.rs
ā āāā middleware/ # Custom middleware
ā ā āāā mod.rs
ā ā āāā auth.rs
ā ā āāā logging.rs
ā āāā extractors/ # Custom extractors
ā āāā mod.rs
āāā services/
ā āāā mod.rs
ā āāā user_service.rs
āāā repositories/
ā āāā mod.rs
ā āāā user_repository.rs
āāā models/
ā āāā mod.rs
ā āāā user.rs
ā āāā dto.rs # Data Transfer Objects
āāā database/
ā āāā mod.rs
ā āāā connection.rs
āāā config/
āāā mod.rs
Complete Axum Example
RUST// src/main.rs use axum::{Router, routing::get, Json}; use serde_json::{Value, json}; #[tokio::main] async fn main() { let app = Router::new() .route("/", get(root)) .route("/health", get(health_check)); let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); axum::serve(listener, app).await.unwrap(); } async fn root() -> Json<Value> { Json(json!({ "message": "Hello, World!" })) } async fn health_check() -> Json<Value> { Json(json!({ "status": "healthy" })) }
Route Organization
RUST// src/api/routes.rs use axum::{Router, routing::{get, post, put, delete}}; use crate::api::handlers::{user_handler, transaction_handler}; pub fn create_router() -> Router { Router::new() .nest("/api/v1", api_v1_routes()) .route("/health", get(health_check)) } fn api_v1_routes() -> Router { Router::new() .nest("/users", user_routes()) .nest("/transactions", transaction_routes()) } fn user_routes() -> Router { Router::new() .route("/", get(user_handler::list_users)) .route("/", post(user_handler::create_user)) .route("/:id", get(user_handler::get_user)) .route("/:id", put(user_handler::update_user)) .route("/:id", delete(user_handler::delete_user)) }
Handler Implementation
RUST// src/api/handlers/user_handler.rs use axum::{extract::{Path, State}, Json, response::Json as ResponseJson}; use crate::{services::UserService, models::dto::CreateUserRequest}; pub async fn create_user( State(service): State<UserService>, Json(payload): Json<CreateUserRequest> ) -> Result<ResponseJson<User>, AppError> { let user = service.create_user(payload).await?; Ok(ResponseJson(user)) } pub async fn get_user( State(service): State<UserService>, Path(id): Path<u64> ) -> Result<ResponseJson<User>, AppError> { let user = service.get_user(id).await?; Ok(ResponseJson(user)) }
Middleware
RUST// src/api/middleware/auth.rs use axum::{extract::Request, middleware::Next, response::Response}; use tower::ServiceBuilder; use tower_http::auth::RequireAuthorizationLayer; pub async fn auth_middleware( mut request: Request, next: Next ) -> Response { // Extract and validate token // Add user to request extensions next.run(request).await } // Apply middleware let app = Router::new() .route("/api/protected", get(protected_route)) .layer(ServiceBuilder::new() .layer(RequireAuthorizationLayer::bearer("secret")) );
Error Handling
RUST// src/error.rs use axum::{response::{Response, IntoResponse}, http::StatusCode}; #[derive(Debug)] pub enum AppError { NotFound, ValidationError(String), DatabaseError(String), Unauthorized, } impl IntoResponse for AppError { fn into_response(self) -> Response { let (status, message) = match self { AppError::NotFound => (StatusCode::NOT_FOUND, "Resource not found"), AppError::ValidationError(msg) => (StatusCode::BAD_REQUEST, &msg), AppError::DatabaseError(msg) => (StatusCode::INTERNAL_SERVER_ERROR, &msg), AppError::Unauthorized => (StatusCode::UNAUTHORIZED, "Unauthorized"), }; (status, Json(json!({ "error": message }))).into_response() } }
Database Integration
RUST// src/database/connection.rs use sqlx::{PgPool, postgres::PgPoolOptions}; pub async fn create_pool(database_url: &str) -> Result<PgPool, sqlx::Error> { PgPoolOptions::new() .max_connections(10) .connect(database_url) .await } // In main.rs let pool = create_pool(&database_url).await?; let app = Router::new() .route("/api/users", get(get_users)) .with_state(pool);
Best Practices
- Route organization: Group related routes
- State management: Use
Stateextractor for dependencies - Error handling: Centralized error types
- Validation: Validate input in handlers
- Middleware: Use for cross-cutting concerns
- Documentation: Use OpenAPI/Swagger
- Testing: Integration tests for API endpoints
Code Examples
Axum API Example
Basic Axum REST API structure
RUST
struct AppState {
}
fn main() {
println!("REST API with Axum:");
println!("1. Router: Define routes");
println!("2. Handlers: Process requests");
println!("3. State: Share dependencies");
println!("4. Middleware: Cross-cutting concerns");
println!("5. Extractors: Parse request data");
}Explanation:
Axum provides a modern, type-safe API for building REST APIs. It uses async/await and integrates well with Tokio.
API Project Structure
Organizing API code
RUST
fn main() {
println!("API Architecture:");
println!("routes ā handlers ā services ā repositories");
println!("Each layer has clear responsibility");
}Explanation:
Well-organized API code separates concerns: routes define endpoints, handlers process requests, services contain business logic, and repositories handle data access.
Exercises
API Route
Create a simple API route structure!
Starter Code:
RUST
fn main() {
println!("API routes created!");
}