Agent skill
backend-rust
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".
Install this agent skill to your Project
npx add-skill https://github.com/timequity/vibe-coder/tree/main/skills/backend-rust
SKILL.md
Rust Backend Stack
π¨ FIRST: Project Protection Setup
MANDATORY 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
Quick Reference
| 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 |
Automation Scripts
| 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.
Tooling
| 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 |
Dependencies
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.
Project Setup
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"
Project Structure
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
Axum Patterns
Basic App
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();
}
App State
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);
Handler with Extractors
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)))
}
Request/Response Types
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,
}
Error Handling
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()
}
}
SQLx Patterns
Database Pool
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
-- 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
Compile-time Checked Queries
// 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?;
Transactions
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?;
Config
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")
}
}
Middleware
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));
Build & CI Workflow
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.
TDD Workflow
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?
Anti-patterns
| 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 |
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
mvp-help
Help and documentation for Idea to MVP plugin. Use when: user asks about building MVPs, vibe coding, or available commands. Triggers: "help", "what can you do", "mvp help", "how to build".
verification-gate
Hidden quality gate that runs before showing "Done!" to user - ensures all tests pass, build succeeds, and requirements met before claiming completion
brainstorming
Refine ideas into detailed designs through Socratic dialogue. Use when: user has rough idea, needs to clarify requirements, explore approaches. Triggers: "brainstorm", "discuss idea", "I'm thinking about", "what if", "help me think through", "explore options", "/brainstorm".
subagent-creator
Guide for creating effective subagents (custom agents). Use when users want to create a new subagent that can be dispatched via Task tool for autonomous work. Covers frontmatter fields (name, description, tools, model, permissionMode, skills), prompt design, and when to use subagents vs skills.
test-driven-development
Write failing test first, then minimal code to pass. Red-Green-Refactor cycle. Use when: implementing features, fixing bugs, refactoring code. Triggers: "implement", "add feature", "fix bug", "tdd", "test first", "write tests", "test-driven".
deploy-automation
One-click deploy to production. User just says "deploy". Use when: user wants to publish their app. Triggers: "deploy", "publish", "go live", "ΠΎΠΏΡΠ±Π»ΠΈΠΊΡΠΉ".
Didn't find tool you were looking for?