rust 57 lines · 8 steps

Building a JSON user API in Axum

A small Axum router wires two async handlers to Postgres, mapping database rows to typed JSON responses.

Explained by highlit
1use axum::{
2 extract::{Path, State},
3 http::StatusCode,
4 routing::get,
5 Json, Router,
6};
7use serde::Serialize;
8use sqlx::PgPool;
9 
10#[derive(Clone)]
11struct AppState {
12 pool: PgPool,
13}
14 
15#[derive(Serialize)]
16struct User {
17 id: i64,
18 email: String,
19 display_name: String,
20}
21 
22pub fn routes(pool: PgPool) -> Router {
23 let state = AppState { pool };
24 Router::new()
25 .route("/users", get(list_users))
26 .route("/users/:id", get(get_user))
27 .with_state(state)
28}
29 
30async fn list_users(State(state): State<AppState>) -> Result<Json<Vec<User>>, StatusCode> {
31 let users = sqlx::query_as!(
32 User,
33 "SELECT id, email, display_name FROM users ORDER BY id LIMIT 100"
34 )
35 .fetch_all(&state.pool)
36 .await
37 .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
38 
39 Ok(Json(users))
40}
41 
42async fn get_user(
43 State(state): State<AppState>,
44 Path(id): Path<i64>,
45) -> Result<Json<User>, StatusCode> {
46 let user = sqlx::query_as!(
47 User,
48 "SELECT id, email, display_name FROM users WHERE id = $1",
49 id
50 )
51 .fetch_optional(&state.pool)
52 .await
53 .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
54 .ok_or(StatusCode::NOT_FOUND)?;
55 
56 Ok(Json(user))
57}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Axum's State extractor threads a shared, cloneable resource like a connection pool through every handler without globals.
  2. 2Returning Result<Json<T>, StatusCode> lets handlers convert database errors and missing rows into clean HTTP responses.
  3. 3sqlx's query_as! maps SQL columns directly into a typed struct, giving you compile-time-checked, serializable results.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a JSON user API in Axum — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code