Introduction
Rust is becoming increasingly popular for web development due to its performance and safety. This guide covers building web services with Rust, from basic setup to production deployment.
Why Rust for Web?
| Feature | Rust | Go | Node.js |
|---|---|---|---|
| Performance | โกโกโก | โกโก | โก |
| Safety | Memory safe | GC | Manual |
| Concurrency | Fearless | Great | Event loop |
| Learning Curve | Steep | Easy | Easy |
| Ecosystem | Growing | Mature | Huge |
Web Frameworks
Axum
Modern, ergonomic web framework:
use axum::{
routing::{get, post},
Router,
};
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize)]
struct User {
name: String,
email: String,
}
async fn hello() -> &'static str {
"Hello, World!"
}
async fn create_user(Json(user): Json<User>) -> Json<User> {
// Save to database...
Json(user)
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/", get(hello))
.route("/users", post(create_user));
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
Actix-web
High-performance, actor-based:
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello, World!")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/", web::get().to(hello))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
Database Integration
With SQLx
use sqlx::{postgres::PgPoolOptions, Row};
#[derive(Debug)]
struct User {
id: i64,
name: String,
email: String,
}
async fn get_users(pool: &PgPool) -> Result<Vec<User>, sqlx::Error> {
let rows = sqlx::query("SELECT id, name, email FROM users")
.fetch_all(pool)
.await?;
Ok(rows
.iter()
.map(|row| User {
id: row.get("id"),
name: row.get("name"),
email: row.get("email"),
})
.collect())
}
With Diesel
use diesel::prelude::*;
use models::User;
fn list_users(conn: &mut PgConnection) -> QueryResult<Vec<User>> {
users::table.load::<User>(conn)
}
Error Handling
use axum::{
extract::Path,
response::Json,
http::StatusCode,
};
async fn get_user(Path(id): Path<i64>) -> Result<Json<User>, AppError> {
let user = db::find_user(id).await?;
match user {
Some(u) => Ok(Json(u)),
None => Err(AppError::NotFound("User not found".into())),
}
}
enum AppError {
NotFound(String),
Database(String),
}
impl From<sqlx::Error> for AppError {
fn from(err: sqlx::Error) -> Self {
AppError::Database(err.to_string())
}
}
Authentication
JWT Auth
use jsonwebtoken::{encode, decode, Header, Validation};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
sub: String,
exp: usize,
}
fn create_token(user_id: &str) -> String {
let expiration = Utc::now() + Duration::hours(24);
let claims = Claims {
sub: user_id.to_string(),
exp: expiration.timestamp() as usize,
};
encode(&Header::default(), &claims, "secret".as_ref()).unwrap()
}
fn validate_token(token: &str) -> Result<Claims, jsonwebtoken::errors::Error> {
let secret = "secret".as_ref();
decode(token, secret, &Validation::default())
.map(|data| data.claims)
}
State Management
use std::sync::Arc;
use tokio::sync::Mutex;
struct AppState {
db: Pool,
cache: Arc<Mutex<LruCache<String, String>>>,
}
async fn handler(
State(state): State<Arc<AppState>>,
) -> impl IntoResponse {
let mut cache = state.cache.lock().await;
// Use cache...
}
Deployment
Docker
# Build stage
FROM rust:1.75 as builder
WORKDIR /app
COPY . .
RUN cargo build --release
# Runtime stage
FROM debian:bookworm-slim
COPY --from=builder /app/target/release/my-app /usr/local/bin/
CMD ["my-app"]
Production Checklist
// 1. Use release profile
// Cargo.toml
[profile.release]
lto = true
codegen-units = 1
opt-level = 3
// 2. Add logging
tracing::info!("Request processed");
// 3. Add metrics
metrics::counter!("requests_total").increment(1);
// 4. Graceful shutdown
use tokio::signal;
use tokio::sync::oneshot;
let (tx, rx) = oneshot::channel::<()>();
tokio::spawn(async {
signal::ctrl_c().await.unwrap();
tx.send(()).unwrap();
});
let _ = rx.await;
Conclusion
Rust for web is excellent when you need:
- Maximum performance
- Memory safety guarantees
- Low-level control
- WebAssembly targets
Start with Axum for simplicity, use Actix-web for performance.
Comments