Building Web API's With Rust 2
Building Web API's With Rust 2
Following up on the first post about building web APIs with Rust, this time we'll explore routing with the reset_router library and then move on to using Axum.
From hyper to Axum
While hyper provides the foundation, building APIs with just hyper requires a lot of boilerplate. Enter Axum - a web framework that builds on hyper and tower.
Basic Axum Setup
use axum::{
routing::get,
Router,
};#[tokio::main]
async fn main() {
let app = Router::new()
.route("/", get(handler));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
.await
.unwrap();
println!("Listening on {}", listener.local_addr().unwrap());
axum::serve(listener, app).await.unwrap();
}
async fn handler() -> &'static str {
"Hello, World!"
}
Routing
let app = Router::new()
.route("/", get(root))
.route("/users", get(list_users).post(create_user))
.route("/users/:id", get(get_user).put(update_user).delete(delete_user));
Extractors
use axum::extract::{Path, Query, Json};
use serde::Deserialize;#[derive(Deserialize)]
struct Pagination {
page: Option<u32>,
per_page: Option<u32>,
}
async fn list_users(
Query(pagination): Query<Pagination>,
) -> Json<Vec<User>> {
// ...
}
async fn get_user(Path(id): Path<u64>) -> Json<User> {
// ...
}
async fn create_user(
Json(user): Json<CreateUserRequest>,
) -> Json<User> {
// ...
}
Middleware
use tower_http::trace::TraceLayer;let app = Router::new()
.route("/", get(handler))
.layer(TraceLayer::new_for_http());
State Management
use axum::extract::State;
use std::sync::Arc;#[derive(Clone)]
struct AppState {
db: DatabaseConnection,
}
let state = Arc::new(AppState { db });
let app = Router::new()
.route("/users", get(list_users))
.with_state(state);
async fn list_users(
State(state): State<Arc<AppState>>,
) -> Json<Vec<User>> {
// Access state.db
}
Error Handling
use axum::response::{IntoResponse, Response};
use axum::http::StatusCode;enum AppError {
DatabaseError(String),
NotFound,
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, message) = match self {
AppError::DatabaseError(e) => {
(StatusCode::INTERNAL_SERVER_ERROR, e)
}
AppError::NotFound => {
(StatusCode::NOT_FOUND, "Not found".to_string())
}
};
(status, message).into_response()
}
}
Axum combines the power of hyper with ergonomic APIs - making Rust web development a joy!