Working Axum implementations for each problem. Useful when adding new problems or verifying the grader.
GET /users → returns all users ordered by id
use axum::{extract::State, routing::get, Json, Router};
use serde::Serialize;
use sqlx::{postgres::PgPoolOptions, PgPool};
#[derive(Serialize, sqlx::FromRow)]
struct User {
id: i32,
name: String,
}
async fn list_users(State(pool): State<PgPool>) -> Json<Vec<User>> {
let users = sqlx::query_as::<_, User>("SELECT id, name FROM users ORDER BY id")
.fetch_all(&pool)
.await
.expect("db query failed");
Json(users)
}
#[tokio::main]
async fn main() {
let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL not set");
let port = std::env::var("PORT").unwrap_or_else(|_| "4000".into());
let pool = PgPoolOptions::new()
.max_connections(5)
.connect(&db_url)
.await
.expect("failed to connect to DB");
let app = Router::new()
.route("/users", get(list_users))
.with_state(pool);
let addr = format!("0.0.0.0:{port}");
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
}GET /users/:id → returns user or 404
use axum::{extract::{Path, State}, http::StatusCode, routing::get, Json, Router};
use serde::Serialize;
use sqlx::{postgres::PgPoolOptions, PgPool};
#[derive(Serialize, sqlx::FromRow)]
struct User {
id: i32,
name: String,
}
async fn get_user(
State(pool): State<PgPool>,
Path(id): Path<i32>,
) -> Result<Json<User>, StatusCode> {
sqlx::query_as::<_, User>("SELECT id, name FROM users WHERE id = $1")
.bind(id)
.fetch_optional(&pool)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
.map(Json)
.ok_or(StatusCode::NOT_FOUND)
}
#[tokio::main]
async fn main() {
let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL not set");
let port = std::env::var("PORT").unwrap_or_else(|_| "4000".into());
let pool = PgPoolOptions::new()
.max_connections(5)
.connect(&db_url)
.await
.expect("failed to connect to DB");
let app = Router::new()
.route("/users/:id", get(get_user))
.with_state(pool);
let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{port}"))
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}POST /users → insert user, return 201
use axum::{extract::State, http::StatusCode, routing::post, Json, Router};
use serde::{Deserialize, Serialize};
use sqlx::{postgres::PgPoolOptions, PgPool};
#[derive(Serialize, sqlx::FromRow)]
struct User {
id: i32,
name: String,
}
#[derive(Deserialize)]
struct CreateUser {
name: String,
}
async fn create_user(
State(pool): State<PgPool>,
Json(body): Json<CreateUser>,
) -> (StatusCode, Json<User>) {
let user = sqlx::query_as::<_, User>(
"INSERT INTO users (name) VALUES ($1) RETURNING id, name",
)
.bind(&body.name)
.fetch_one(&pool)
.await
.expect("insert failed");
(StatusCode::CREATED, Json(user))
}
#[tokio::main]
async fn main() {
let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL not set");
let port = std::env::var("PORT").unwrap_or_else(|_| "4000".into());
let pool = PgPoolOptions::new()
.max_connections(5)
.connect(&db_url)
.await
.expect("failed to connect to DB");
let app = Router::new()
.route("/users", post(create_user))
.with_state(pool);
let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{port}"))
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}GET /stats → aggregate user count
use axum::{extract::State, routing::get, Json, Router};
use serde::Serialize;
use sqlx::{postgres::PgPoolOptions, PgPool, Row};
#[derive(Serialize)]
struct Stats {
total_users: i64,
}
async fn user_stats(State(pool): State<PgPool>) -> Json<Stats> {
let row = sqlx::query("SELECT COUNT(*) as count FROM users")
.fetch_one(&pool)
.await
.expect("query failed");
Json(Stats {
total_users: row.get::<i64, _>("count"),
})
}
#[tokio::main]
async fn main() {
let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL not set");
let port = std::env::var("PORT").unwrap_or_else(|_| "4000".into());
let pool = PgPoolOptions::new()
.max_connections(5)
.connect(&db_url)
.await
.expect("failed to connect to DB");
let app = Router::new()
.route("/stats", get(user_stats))
.with_state(pool);
let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{port}"))
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}PUT /users/:id → update user or 404
use axum::{extract::{Path, State}, http::StatusCode, routing::put, Json, Router};
use serde::{Deserialize, Serialize};
use sqlx::{postgres::PgPoolOptions, PgPool};
#[derive(Serialize, sqlx::FromRow)]
struct User {
id: i32,
name: String,
}
#[derive(Deserialize)]
struct UpdateUser {
name: String,
}
async fn update_user(
State(pool): State<PgPool>,
Path(id): Path<i32>,
Json(body): Json<UpdateUser>,
) -> Result<Json<User>, StatusCode> {
sqlx::query_as::<_, User>(
"UPDATE users SET name = $1 WHERE id = $2 RETURNING id, name",
)
.bind(&body.name)
.bind(id)
.fetch_optional(&pool)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
.map(Json)
.ok_or(StatusCode::NOT_FOUND)
}
#[tokio::main]
async fn main() {
let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL not set");
let port = std::env::var("PORT").unwrap_or_else(|_| "4000".into());
let pool = PgPoolOptions::new()
.max_connections(5)
.connect(&db_url)
.await
.expect("failed to connect to DB");
let app = Router::new()
.route("/users/:id", put(update_user))
.with_state(pool);
let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{port}"))
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}