.claude/skills/ts-actix-web/SKILL.md
Actix Web is a powerful, high-performance web framework for Rust. It provides async handlers, type-safe extractors, middleware, and integrates with the Rust ecosystem for building fast, reliable, and memory-safe web services.
npx skillsauth add eliferjunior/Claude actix-webInstall 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.
Actix Web is one of the fastest web frameworks available. It uses Rust's type system for compile-time safety, async/await for concurrency, and extractors for ergonomic request handling.
# Cargo.toml — dependencies
[dependencies]
actix-web = "4"
actix-rt = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sqlx = { version = "0.7", features = ["runtime-tokio", "postgres", "macros"] }
tokio = { version = "1", features = ["full"] }
env_logger = "0.11"
# Recommended Actix Web project layout
src/
├── main.rs # Entry point and server config
├── config.rs # Configuration
├── routes/
│ ├── mod.rs # Route registration
│ ├── articles.rs # Article handlers
│ └── health.rs # Health check
├── models/
│ └── article.rs # Data structures
├── db/
│ └── article.rs # Database queries
├── middleware/
│ └── auth.rs # Auth middleware
└── errors.rs # Error types
// src/main.rs — application entry point
use actix_web::{web, App, HttpServer, middleware::Logger};
use sqlx::postgres::PgPoolOptions;
mod routes;
mod models;
mod db;
mod errors;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
env_logger::init();
let database_url = std::env::var("DATABASE_URL")
.unwrap_or_else(|_| "postgres://localhost/mydb".to_string());
let pool = PgPoolOptions::new()
.max_connections(10)
.connect(&database_url)
.await
.expect("Failed to create pool");
HttpServer::new(move || {
App::new()
.wrap(Logger::default())
.app_data(web::Data::new(pool.clone()))
.configure(routes::configure)
})
.bind("0.0.0.0:8080")?
.run()
.await
}
// src/routes/mod.rs — centralized route registration
use actix_web::web;
mod articles;
mod health;
pub fn configure(cfg: &mut web::ServiceConfig) {
cfg.service(
web::scope("/api")
.route("/health", web::get().to(health::check))
.service(
web::scope("/articles")
.route("", web::get().to(articles::list))
.route("", web::post().to(articles::create))
.route("/{id}", web::get().to(articles::get))
.route("/{id}", web::delete().to(articles::delete))
)
);
}
// src/models/article.rs — data models with serde
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
#[derive(Debug, Serialize, FromRow)]
pub struct Article {
pub id: i32,
pub title: String,
pub body: String,
pub published: bool,
pub created_at: chrono::NaiveDateTime,
}
#[derive(Debug, Deserialize)]
pub struct CreateArticle {
pub title: String,
pub body: String,
}
#[derive(Debug, Deserialize)]
pub struct ListParams {
pub page: Option<u32>,
pub limit: Option<u32>,
}
// src/routes/articles.rs — request handlers
use actix_web::{web, HttpResponse};
use sqlx::PgPool;
use crate::models::article::{Article, CreateArticle, ListParams};
use crate::errors::AppError;
pub async fn list(
pool: web::Data<PgPool>,
query: web::Query<ListParams>,
) -> Result<HttpResponse, AppError> {
let limit = query.limit.unwrap_or(20).min(100) as i64;
let offset = ((query.page.unwrap_or(1) - 1) * limit as u32) as i64;
let articles = sqlx::query_as::<_, Article>(
"SELECT * FROM articles WHERE published = true ORDER BY created_at DESC LIMIT $1 OFFSET $2"
)
.bind(limit)
.bind(offset)
.fetch_all(pool.get_ref())
.await?;
Ok(HttpResponse::Ok().json(articles))
}
pub async fn create(
pool: web::Data<PgPool>,
body: web::Json<CreateArticle>,
) -> Result<HttpResponse, AppError> {
let article = sqlx::query_as::<_, Article>(
"INSERT INTO articles (title, body) VALUES ($1, $2) RETURNING *"
)
.bind(&body.title)
.bind(&body.body)
.fetch_one(pool.get_ref())
.await?;
Ok(HttpResponse::Created().json(article))
}
pub async fn get(
pool: web::Data<PgPool>,
path: web::Path<i32>,
) -> Result<HttpResponse, AppError> {
let id = path.into_inner();
let article = sqlx::query_as::<_, Article>("SELECT * FROM articles WHERE id = $1")
.bind(id)
.fetch_optional(pool.get_ref())
.await?
.ok_or(AppError::NotFound)?;
Ok(HttpResponse::Ok().json(article))
}
// src/errors.rs — custom error types
use actix_web::{HttpResponse, ResponseError};
use std::fmt;
#[derive(Debug)]
pub enum AppError {
NotFound,
Internal(String),
Database(sqlx::Error),
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AppError::NotFound => write!(f, "Not found"),
AppError::Internal(msg) => write!(f, "{}", msg),
AppError::Database(e) => write!(f, "Database error: {}", e),
}
}
}
impl ResponseError for AppError {
fn error_response(&self) -> HttpResponse {
match self {
AppError::NotFound => HttpResponse::NotFound().json(serde_json::json!({"error": "not found"})),
_ => HttpResponse::InternalServerError().json(serde_json::json!({"error": "internal error"})),
}
}
}
impl From<sqlx::Error> for AppError {
fn from(e: sqlx::Error) -> Self { AppError::Database(e) }
}
// src/middleware/auth.rs — simple auth middleware
use actix_web::{dev::ServiceRequest, Error, HttpMessage};
use actix_web::error::ErrorUnauthorized;
pub async fn validate_token(req: &ServiceRequest) -> Result<(), Error> {
let token = req.headers()
.get("Authorization")
.and_then(|v| v.to_str().ok())
.and_then(|v| v.strip_prefix("Bearer "));
match token {
Some(t) => {
let user_id = verify_jwt(t).map_err(|_| ErrorUnauthorized("invalid token"))?;
req.extensions_mut().insert(user_id);
Ok(())
}
None => Err(ErrorUnauthorized("missing token")),
}
}
// tests/articles_test.rs — integration test
use actix_web::{test, App, web};
#[actix_web::test]
async fn test_list_articles() {
let pool = setup_test_db().await;
let app = test::init_service(
App::new()
.app_data(web::Data::new(pool))
.configure(routes::configure)
).await;
let req = test::TestRequest::get().uri("/api/articles").to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), 200);
}
web::Json, web::Path, web::Query, web::Data) for type-safe request parsingResponseError on custom error types for automatic HTTP error responsesweb::Data for shared application state (DB pool, config) — it's cheaply cloneablesqlx with compile-time checked queries (sqlx::query!) for production codeconfigure functions to modularize route registration#[actix_web::test] for async integration tests with test::init_servicedevelopment
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.