Modern Rust backend with Axum, SQLx, tokio + CI/CD automation. Use when: building Rust APIs, high-performance services, or needing build/test/lint/audit automation. Triggers: "axum", "rust backend", "rust api", "sqlx", "tokio", "cargo build", "cargo test", "clippy", "rustfmt", "cargo-audit", "cross-compile", "rust ci", "release build", "rust security", "shuttle", "actix".
/plugin marketplace add timequity/vibe-coder/plugin install vibe-coder@vibe-coderThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/ci-cd.mdreferences/cross-compile.mdreferences/testing.mdscripts/audit.pyscripts/build.pyscripts/check.pyscripts/lint.pyscripts/tdd_gate.pyscripts/test.pyMANDATORY before writing any code. Run this on every new project:
# 1. Create .gitignore (ALWAYS include these)
cat >> .gitignore << 'EOF'
# Build artifacts
target/
Cargo.lock
# IDE
.idea/
.vscode/
.DS_Store
# Secrets
.env
.env.*
!.env.example
*.key
*.pem
credentials.json
EOF
# 2. Check pre-commit installed
which pre-commit || pip install pre-commit
# 3. Setup pre-commit config
cat > .pre-commit-config.yaml << 'EOF'
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-added-large-files
args: ['--maxkb=500']
- id: detect-private-key
- repo: https://github.com/gitleaks/gitleaks
rev: v8.21.2
hooks:
- id: gitleaks
EOF
# 4. Install hooks
pre-commit install
# 5. Verify
git status # Should show .gitignore and .pre-commit-config.yaml
Why mandatory: Prevents accidental commit of target/ (2GB+), secrets, credentials.
For comprehensive secret protection, use: skill: secrets-guardian
| Topic | Reference |
|---|---|
| Testing | testing.md ā axum-test, mockall, async tests |
| CI/CD | ci-cd.md ā GitHub Actions, Docker, caching |
| Cross-Compile | cross-compile.md ā targets, musl, static binaries |
| Script | Purpose | Usage |
|---|---|---|
build.py | Build debug/release | python scripts/build.py --release |
test.py | Run tests + coverage | python scripts/test.py --coverage |
lint.py | Format + clippy | python scripts/lint.py --fix |
audit.py | Security audit | python scripts/audit.py |
check.py | Full CI check | python scripts/check.py |
Scripts auto-detect Cargo.toml. Use --path to specify location.
| Tool | Purpose | Why |
|---|---|---|
| Axum | Web framework | Tower ecosystem, ergonomic |
| SQLx | Database | Compile-time checked queries |
| tokio | Async runtime | Industry standard |
| serde | Serialization | JSON, TOML, etc. |
| tracing | Logging | Structured, async-aware |
| Shuttle | Deploy | Free tier, simple |
CRITICAL: Always use Context7 to check latest crate versions before adding dependencies:
mcp__context7__resolve-library-id ā mcp__context7__get-library-docs
Versions in this skill are examples ā verify current versions via Context7 or crates.io.
Rust version: 1.85+ required (edition 2024 support). Current stable: 1.91.0.
NOTE: Rust edition 2024 is stable since Rust 1.85 (February 2025). Always use edition = "2024" for new projects.
cargo new my-api
cd my-api
# Cargo.toml
[package]
name = "my-api"
version = "0.1.0"
edition = "2024" # Stable since Rust 1.85 (Feb 2025)
[dependencies]
axum = "0.8"
tokio = { version = "1", features = ["full"] }
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "uuid", "chrono"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tower-http = { version = "0.5", features = ["cors", "trace"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
dotenvy = "0.15"
thiserror = "1"
uuid = { version = "1", features = ["v4", "serde"] }
chrono = { version = "0.4", features = ["serde"] }
[dev-dependencies]
axum-test = "15"
src/
āāā main.rs # Entry point
āāā config.rs # Environment config
āāā db.rs # Database pool
āāā error.rs # Error types
āāā routes/
ā āāā mod.rs
ā āāā health.rs
ā āāā users.rs
āāā models/
ā āāā mod.rs
ā āāā user.rs
āāā handlers/
ā āāā users.rs
āāā middleware/
āāā auth.rs
migrations/
āāā 001_create_users.sql
tests/
āāā api_tests.rs
use axum::{routing::get, Router};
use std::net::SocketAddr;
use tower_http::trace::TraceLayer;
#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
let app = Router::new()
.route("/health", get(|| async { "OK" }))
.layer(TraceLayer::new_for_http());
let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
}
use axum::extract::FromRef;
use sqlx::PgPool;
#[derive(Clone, FromRef)]
pub struct AppState {
pub db: PgPool,
pub config: Config,
}
// In main.rs
let state = AppState { db: pool, config };
let app = Router::new()
.route("/users", get(list_users).post(create_user))
.with_state(state);
use axum::{
extract::{Path, State, Json},
http::StatusCode,
response::IntoResponse,
};
use sqlx::PgPool;
pub async fn get_user(
State(db): State<PgPool>,
Path(id): Path<i32>,
) -> Result<Json<User>, AppError> {
let user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", id)
.fetch_optional(&db)
.await?
.ok_or(AppError::NotFound)?;
Ok(Json(user))
}
pub async fn create_user(
State(db): State<PgPool>,
Json(input): Json<CreateUser>,
) -> Result<impl IntoResponse, AppError> {
let user = sqlx::query_as!(
User,
"INSERT INTO users (email, name) VALUES ($1, $2) RETURNING *",
input.email,
input.name
)
.fetch_one(&db)
.await?;
Ok((StatusCode::CREATED, Json(user)))
}
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
use uuid::Uuid;
#[derive(Serialize, sqlx::FromRow)]
pub struct User {
pub id: i32,
pub public_id: Uuid,
pub email: String,
pub name: String,
pub created_at: DateTime<Utc>,
}
#[derive(Deserialize)]
pub struct CreateUser {
pub email: String,
pub name: String,
}
#[derive(Serialize)]
pub struct UserList {
pub data: Vec<User>,
pub total: i64,
}
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
use serde_json::json;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
#[error("Not found")]
NotFound,
#[error("Validation error: {0}")]
Validation(String),
#[error("Database error")]
Database(#[from] sqlx::Error),
#[error("Internal error")]
Internal(#[from] anyhow::Error),
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, message) = match &self {
AppError::NotFound => (StatusCode::NOT_FOUND, "Not found"),
AppError::Validation(msg) => (StatusCode::BAD_REQUEST, msg.as_str()),
AppError::Database(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Database error"),
AppError::Internal(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Internal error"),
};
let body = Json(json!({ "error": { "message": message } }));
(status, body).into_response()
}
}
use sqlx::postgres::PgPoolOptions;
pub async fn create_pool(database_url: &str) -> sqlx::PgPool {
PgPoolOptions::new()
.max_connections(5)
.connect(database_url)
.await
.expect("Failed to create pool")
}
-- migrations/001_create_users.sql
CREATE TABLE users (
id SERIAL PRIMARY KEY,
public_id UUID DEFAULT gen_random_uuid() NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
);
CREATE INDEX idx_users_email ON users(email);
# Run migrations
sqlx migrate run
# Create new migration
sqlx migrate add create_posts
// Requires DATABASE_URL in .env for compile-time checking
let users = sqlx::query_as!(
User,
r#"
SELECT id, public_id, email, name, created_at
FROM users
WHERE email LIKE $1
ORDER BY created_at DESC
LIMIT $2 OFFSET $3
"#,
format!("%{}%", search),
limit,
offset
)
.fetch_all(&pool)
.await?;
let mut tx = pool.begin().await?;
sqlx::query!("INSERT INTO users (email, name) VALUES ($1, $2)", email, name)
.execute(&mut *tx)
.await?;
sqlx::query!("INSERT INTO profiles (user_id, bio) VALUES ($1, $2)", user_id, bio)
.execute(&mut *tx)
.await?;
tx.commit().await?;
use serde::Deserialize;
#[derive(Clone, Deserialize)]
pub struct Config {
pub database_url: String,
pub port: u16,
pub jwt_secret: String,
}
impl Config {
pub fn from_env() -> Self {
dotenvy::dotenv().ok();
envy::from_env().expect("Failed to load config")
}
}
use axum::{
extract::Request,
http::{header, StatusCode},
middleware::Next,
response::Response,
};
pub async fn auth_middleware(
request: Request,
next: Next,
) -> Result<Response, StatusCode> {
let auth_header = request
.headers()
.get(header::AUTHORIZATION)
.and_then(|h| h.to_str().ok())
.ok_or(StatusCode::UNAUTHORIZED)?;
if !auth_header.starts_with("Bearer ") {
return Err(StatusCode::UNAUTHORIZED);
}
// Validate token...
Ok(next.run(request).await)
}
// Apply to routes
let protected = Router::new()
.route("/me", get(get_current_user))
.layer(axum::middleware::from_fn(auth_middleware));
Full CI check before commits:
python scripts/check.py
Individual steps:
# Format and lint (auto-fix)
python scripts/lint.py --fix
# Run tests
python scripts/test.py
# Security audit
python scripts/audit.py
# Release build
python scripts/build.py --release
# Cross-compile for Linux (static binary)
python scripts/build.py --release --target x86_64-unknown-linux-musl
For CI/CD pipelines and cross-compilation setup, see ci-cd.md.
Use agents for Test-Driven Development:
1. tdd-test-writer ā writes failing test (RED)
2. Verify: cargo test ā see failure
3. rust-developer ā implements minimal code (GREEN + self-review)
4. Verify: cargo test ā see pass
5. Repeat for all features
6. code-reviewer ā final review before commit/merge
Full cycle example:
# Per feature (TDD cycles)
Task[tdd-test-writer]: "GET /users endpoint" # RED
Task[rust-developer]: "make test pass" # GREEN + self-review
# After all features complete
Task[code-reviewer]: "review all changes" # REVIEW
git add && git commit # COMMIT
Enable TDD enforcement hook (blocks implementation without failing test):
// .claude/settings.json
{
"hooks": {
"PreToolUse": [{
"matcher": "Task",
"hooks": [{
"type": "command",
"command": "python3 scripts/tdd_gate.py"
}]
}]
}
}
Review checklist (used by rust-developer self-review and code-reviewer):
ā” Logic correct? Edge cases handled?
ā” Security: no injection, no hardcoded secrets?
ā” Error handling: no unwrap() in handlers?
ā” Patterns: follows skill guidelines?
ā” Tests: all pass, coverage adequate?
| Don't | Do Instead |
|---|---|
unwrap() in handlers | Use ? with proper error types |
clone() everywhere | Use Arc<T> for shared state |
| Raw SQL strings | Use sqlx::query! macro |
println! | Use tracing::info! |
| Blocking code in async | Use spawn_blocking |
| Manual JSON | Use serde derive |
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.