⏳
Loading cheatsheet...
Actix-web, Axum, Tokio, async runtimes, middleware, database integration, and web APIs.
// ── Cargo.toml ──
// [dependencies]
// actix-web = "4"
// actix-rt = "2"
// serde = { version = "1", features = ["derive"] }
// serde_json = "1"
use actix_web::{web, App, HttpServer, HttpResponse, HttpMessage, Responder};
use serde::{Deserialize, Serialize};
// ── Handler Function ──
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello, Actix-Web!")
}
// ── Path Parameters ──
#[derive(Deserialize)]
struct UserPath {
id: u32,
}
async fn get_user(path: web::Path<UserPath>) -> impl Responder {
HttpResponse::Ok().json(serde_json::json!({
"id": path.id,
"name": "Alice"
}))
}
// ── Query Parameters ──
#[derive(Deserialize)]
struct Pagination {
page: Option<u32>,
limit: Option<u32>,
}
async fn list_users(query: web::Query<Pagination>) -> impl Responder {
let page = query.page.unwrap_or(1);
let limit = query.limit.unwrap_or(20);
HttpResponse::Ok().body(format!("Page {}, Limit {}", page, limit))
}
// ── JSON Request Body ──
#[derive(Deserialize, Serialize)]
struct CreateUser {
name: String,
email: String,
}
async fn create_user(body: web::Json<CreateUser>) -> impl Responder {
HttpResponse::Created().json(serde_json::json!({
"id": 1,
"name": body.name,
"email": body.email,
}))
}
// ── Application Setup ──
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/", web::get().to(hello))
.route("/users", web::get().to(list_users))
.route("/users", web::post().to(create_user))
.route("/users/{id}", web::get().to(get_user))
})
.bind("127.0.0.1:8080")?
.run()
.await
}// ── Cargo.toml ──
// [dependencies]
// axum = "0.7"
// tokio = { version = "1", features = ["full"] }
// serde = { version = "1", features = ["derive"] }
// serde_json = "1"
// tower-http = { version = "0.5", features = ["cors"] }
use axum::{
routing::{get, post},
Router, Json,
extract::{Path, Query, State},
http::StatusCode,
response::IntoResponse,
};
use serde::{Deserialize, Serialize};
// ── Shared Application State ──
#[derive(Clone)]
struct AppState {
db: sqlx::PgPool,
}
// ── Handler with State ──
async fn get_users(
State(state): State<AppState>,
) -> impl IntoResponse {
let users = sqlx::query_as!(User, "SELECT * FROM users")
.fetch_all(&state.db)
.await
.unwrap();
(StatusCode::OK, Json(users)).into_response()
}
// ── Path Extraction ──
#[derive(Deserialize)]
struct UserParams {
id: u32,
}
async fn get_user(Path(params): Path<UserParams>) -> Json<Value> {
Json(serde_json::json!({ "id": params.id }))
}
// ── JSON Body ──
#[derive(Deserialize)]
struct NewUser { name: String, email: String }
async fn create_user(Json(payload): Json<NewUser>) {
Json(payload) // returns as response
}
// ── Router Setup ──
let app = Router::new()
.route("/", get(hello))
.route("/users", get(get_users).post(create_user))
.route("/users/:id", get(get_user))
.with_state(app_state);
// ── Run Server ──
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
axum::serve(listener, app).await?;| Feature | Axum | Actix-Web |
|---|---|---|
| Based on | Tower ecosystem | Actix runtime |
| Type safety | Strong extractors | Request/Response types |
| Async runtime | Tokio (required) | Actix-rt |
| Learning curve | Moderate | Moderate |
| Performance | Excellent | Excellent |
| Community | Growing fast | Mature |
| Extractor | From |
|---|---|
| Path<T> | URL path parameters |
| Query<T> | Query string parameters |
| Json<T> | JSON request body |
| State<S> | Application state |
| Form<T> | URL-encoded form |
| Header<T> | Request headers |
| Extension | Custom typed headers |
// ── Axum Middleware (Tower Layer) ──
use tower_http::cors::{CorsLayer, Any};
use axum::middleware;
// CORS middleware
let cors = CorsLayer::new()
.allow_origin(Any)
.allow_methods([Method::GET, Method::POST])
.allow_headers(Any);
// Custom middleware function
async fn logging_middleware(
req: axum::extract::Request,
next: middleware::Next,
) -> axum::response::Response {
println!("{} {}", req.method(), req.uri());
next.run(req).await
}
// Apply to router
let app = Router::new()
.route("/api/*", get(handler))
.layer(cors)
.layer(middleware::from_fn(logging_middleware));
// ── JWT Authentication ──
use jsonwebtoken::{encode, decode, Header, Encoding, Key};
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
sub: String,
exp: usize,
iat: usize,
}
fn create_token(user_id: &str) -> String {
let claims = Claims {
sub: user_id.to_string(),
exp: Utc::now().timestamp() + 86400,
iat: Utc::now().timestamp(),
};
encode(&Header::default(), &claims,
&Key::from_secret("your-secret"))
.unwrap()
}
fn verify_token(token: &str) -> Result<Claims, jsonwebtoken::errors::Error> {
let token_data = decode::<Claims>(
token,
&Key::from_secret("your-secret"),
&Validation::new(Algorithm::HS256),
)?;
Ok(token_data.claims)
}