Claude Code Plugins

Community-maintained marketplace

Feedback

backend-rust

@timequity/plugins
0
0

|

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name backend-rust
description 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".

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

cargo new my-api
cd my-api
# Cargo.toml
[package]
name = "my-api"
version = "0.1.0"
edition = "2024"

[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