daiki48/dotfiles

axum-guide

Axum v0.8.x backend development guide. Handlers, routing, middleware, auth, error handling.

First seen Feb 21, 2026

Installation

$ npx skills add daiki48/dotfiles --skill axum-guide

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from daiki48/dotfiles.

npx skills add daiki48/dotfiles

Browse all from daiki48/dotfiles

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Default branch main
Open issues 2
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 4,028 B
  • docs SUMMARY.md 109 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 17 installs

SKILL.md

Axum v0.8.x Development Guide

Handlers

use axum::{extract::{Path, Query, State, Json}, response::IntoResponse, http::StatusCode};

async fn get_user(
    State(pool): State<PgPool>,
    Path(id): Path<i32>,
) -> Result<Json<User>, AppError> {
    let user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", id)
        .fetch_one(&pool).await?;
    Ok(Json(user))
}

async fn create_user(
    State(pool): State<PgPool>,
    claims: Claims,  // Custom extractor
    Json(payload): Json<CreateUserRequest>,
) -> Result<(StatusCode, Json<User>), AppError> {
    // ...
    Ok((StatusCode::CREATED, Json(user)))
}

Routing

fn app() -> Router<AppState> {
    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))
        .nest("/api", api_routes())
        .layer(middleware_stack())
        .with_state(state)
}

Error Handling

pub struct AppError {
    pub code: StatusCode,
    pub message: String,
}

impl IntoResponse for AppError {
    fn into_response(self) -> Response {
        (self.code, Json(json!({"error": self.message}))).into_response()
    }
}

impl From<sqlx::Error> for AppError {
    fn from(err: sqlx::Error) -> Self {
        match err {
            sqlx::Error::RowNotFound => AppError {
                code: StatusCode::NOT_FOUND,
                message: "Resource not found".into(),
            },
            _ => AppError {
                code: StatusCode::INTERNAL_SERVER_ERROR,
                message: "Database error".into(),
            },
        }
    }
}

Middleware

async fn logging_middleware(req: Request, next: Next) -> Response {
    let method = req.method().clone();
    let uri = req.uri().clone();
    let response = next.run(req).await;
    tracing::info!("{} {} -> {}", method, uri, response.status());
    response
}

let app = Router::new()
    .route("/", get(handler))
    .layer(middleware::from_fn(logging_middleware));

Custom Extractor

pub struct Claims { pub user_id: i32, pub role: String }

#[async_trait]
impl<S: Send + Sync> FromRequestParts<S> for Claims {
    type Rejection = AppError;

    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
        let token = parts.headers.get("cookie")
            .and_then(|v| v.to_str().ok())
            .and_then(extract_token)
            .ok_or(AppError::unauthorized("No token"))?;
        validate_jwt(&token)
    }
}

Auth Middleware

async fn auth_middleware(claims: Claims, req: Request, next: Next) -> Response {
    next.run(req).await
}

let protected = Router::new()
    .route("/profile", get(profile))
    .layer(middleware::from_fn(auth_middleware));

CORS

use tower_http::cors::{CorsLayer, Any};

let cors = CorsLayer::new()
    .allow_origin(Any)
    .allow_methods(Any)
    .allow_headers(Any)
    .allow_credentials(true);

Response Headers & Cookies

async fn login() -> impl IntoResponse {
    let cookie = format!(
        "token={}; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age={}",
        token, max_age
    );
    ([(header::SET_COOKIE, cookie)], Json(response))
}

File Upload

async fn upload(mut multipart: Multipart) -> Result<(), AppError> {
    while let Some(field) = multipart.next_field().await? {
        let name = field.name().unwrap_or_default().to_string();
        let data = field.bytes().await?;
        // Process file
    }
    Ok(())
}

Notes

  1. Extractor order: Put State last (others may consume body)
  2. Consistent error type: Use single AppError across app
  3. Middleware order: Executes outer→inner (reverse of layer order)
  4. State type: Must implement Clone (usually wrap in Arc)