.claude/skills/ts-axum/SKILL.md
You are an expert in Axum, the web framework built on top of Tokio and Tower by the Tokio team. You help developers build high-performance, type-safe APIs and web services using Axum's extractor-based handler system, middleware via Tower layers, WebSocket support, and compile-time route validation — achieving C-level performance with Rust's memory safety guarantees.
npx skillsauth add eliferjunior/Claude axumInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are an expert in Axum, the web framework built on top of Tokio and Tower by the Tokio team. You help developers build high-performance, type-safe APIs and web services using Axum's extractor-based handler system, middleware via Tower layers, WebSocket support, and compile-time route validation — achieving C-level performance with Rust's memory safety guarantees.
// src/main.rs — Axum API server
use axum::{
Router, Json, Extension,
extract::{Path, Query, State},
http::StatusCode,
routing::{get, post, put, delete},
middleware,
};
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
use std::sync::Arc;
use tower_http::cors::CorsLayer;
use tower_http::trace::TraceLayer;
#[derive(Clone)]
struct AppState {
db: PgPool,
redis: redis::Client,
}
#[tokio::main]
async fn main() {
tracing_subscriber::init();
let db = PgPool::connect(&std::env::var("DATABASE_URL").unwrap())
.await.unwrap();
let state = AppState {
db,
redis: redis::Client::open("redis://127.0.0.1/").unwrap(),
};
let app = Router::new()
.route("/users", get(list_users).post(create_user))
.route("/users/{id}", get(get_user).put(update_user).delete(delete_user))
.route("/health", get(|| async { "OK" }))
.layer(CorsLayer::permissive())
.layer(TraceLayer::new_for_http())
.with_state(state);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
// Extractors pull data from requests — type-safe at compile time
#[derive(Deserialize)]
struct CreateUserRequest {
name: String,
email: String,
}
#[derive(Serialize)]
struct UserResponse {
id: i64,
name: String,
email: String,
created_at: chrono::NaiveDateTime,
}
// State, Path, Query, Json are all extractors
async fn create_user(
State(state): State<AppState>, // Application state
Json(payload): Json<CreateUserRequest>, // Request body
) -> Result<(StatusCode, Json<UserResponse>), AppError> {
let user = sqlx::query_as!(
UserResponse,
"INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *",
payload.name,
payload.email,
)
.fetch_one(&state.db)
.await?;
Ok((StatusCode::CREATED, Json(user)))
}
async fn get_user(
State(state): State<AppState>,
Path(id): Path<i64>, // URL path parameter
) -> Result<Json<UserResponse>, AppError> {
let user = sqlx::query_as!(UserResponse, "SELECT * FROM users WHERE id = $1", id)
.fetch_optional(&state.db)
.await?
.ok_or(AppError::NotFound)?;
Ok(Json(user))
}
#[derive(Deserialize)]
struct ListParams {
page: Option<u32>,
per_page: Option<u32>,
}
async fn list_users(
State(state): State<AppState>,
Query(params): Query<ListParams>, // Query string parameters
) -> Result<Json<Vec<UserResponse>>, AppError> {
let page = params.page.unwrap_or(1);
let per_page = params.per_page.unwrap_or(20).min(100);
let offset = ((page - 1) * per_page) as i64;
let users = sqlx::query_as!(
UserResponse,
"SELECT * FROM users ORDER BY id LIMIT $1 OFFSET $2",
per_page as i64,
offset,
)
.fetch_all(&state.db)
.await?;
Ok(Json(users))
}
use axum::response::IntoResponse;
enum AppError {
NotFound,
Database(sqlx::Error),
Unauthorized,
}
impl IntoResponse for AppError {
fn into_response(self) -> axum::response::Response {
let (status, message) = match self {
AppError::NotFound => (StatusCode::NOT_FOUND, "Resource not found"),
AppError::Database(e) => {
tracing::error!("Database error: {e}");
(StatusCode::INTERNAL_SERVER_ERROR, "Internal server error")
}
AppError::Unauthorized => (StatusCode::UNAUTHORIZED, "Unauthorized"),
};
(status, Json(serde_json::json!({ "error": message }))).into_response()
}
}
impl From<sqlx::Error> for AppError {
fn from(e: sqlx::Error) -> Self { AppError::Database(e) }
}
use axum::middleware::Next;
use axum::http::Request;
async fn auth_middleware(
State(state): State<AppState>,
mut req: Request<axum::body::Body>,
next: Next,
) -> Result<impl IntoResponse, AppError> {
let token = req.headers()
.get("Authorization")
.and_then(|v| v.to_str().ok())
.and_then(|v| v.strip_prefix("Bearer "))
.ok_or(AppError::Unauthorized)?;
let user = validate_token(&state.db, token).await?;
req.extensions_mut().insert(user);
Ok(next.run(req).await)
}
// Apply to specific routes
let protected = Router::new()
.route("/profile", get(get_profile))
.layer(middleware::from_fn_with_state(state.clone(), auth_middleware));
# Cargo.toml
[dependencies]
axum = "0.8"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres"] }
tower-http = { version = "0.6", features = ["cors", "trace"] }
tracing = "0.1"
tracing-subscriber = "0.3"
query_as! macro; catches SQL errors before runtimeIntoResponse for error enums; consistent error responses across all handlerswith_state() for database pools, config, caches; cloned cheaply via Arc internallytokio::signal to handle SIGTERM; Axum drains connections before stoppingextract::ws::WebSocket; integrates with Tower middlewaredevelopment
Expert guidance for Fireworks AI, the platform for running open-source LLMs (Llama, Mixtral, Qwen, etc.) with enterprise-grade speed and reliability. Helps developers integrate Fireworks' inference API, fine-tune models, and deploy custom model endpoints with function calling and structured output support.
development
Convert any website into clean, structured data with Firecrawl — API-first web scraping service. Use when someone asks to "turn a website into markdown", "scrape website for LLM", "Firecrawl", "extract website content as clean text", "crawl and convert to structured data", or "scrape website for RAG". Covers single-page scraping, full-site crawling, structured extraction, and LLM-ready output.
tools
Expert guidance for Firebase, Google's platform for building and scaling web and mobile applications. Helps developers set up authentication, Firestore/Realtime Database, Cloud Functions, hosting, storage, and analytics using Firebase's SDK and CLI.
development
When the user needs to build file upload functionality for a web application. Use when the user mentions "file upload," "image upload," "upload endpoint," "multipart upload," "presigned URL," "S3 upload," "file validation," "upload to cloud storage," or "accept user files." Handles upload endpoints, file validation (type, size, magic bytes), cloud storage integration, and upload status tracking. For image/video processing after upload, see media-transcoder.