internal/skills/content/actix-web/SKILL.md
Actix-web framework guardrails, patterns, and best practices for AI-assisted development. Use when working with Actix-web projects, or when the user mentions Actix-web. Provides actor patterns, async handlers, extractors, and high-performance web guidelines.
npx skillsauth add ar4mirez/samuel 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.
Applies to: Actix-web 4+, Rust Web APIs, High-Performance Services Complements:
.claude/skills/rust-guide/SKILL.md
web::Json, web::Path, web::Query) for request dataResponseError for all error types; never return raw stringsweb::Data: Application state is injected, never globalmyproject/
├── Cargo.toml
├── src/
│ ├── main.rs # Entry point, server bootstrap
│ ├── config.rs # Configuration loading
│ ├── routes.rs # Route registration
│ ├── handlers/
│ │ ├── mod.rs
│ │ ├── users.rs # User-related handlers
│ │ └── health.rs # Health check endpoint
│ ├── models/
│ │ ├── mod.rs
│ │ └── user.rs # Domain models + DTOs
│ ├── services/
│ │ ├── mod.rs
│ │ └── user_service.rs # Business logic layer
│ ├── repositories/
│ │ ├── mod.rs
│ │ └── user_repository.rs # Database access layer
│ ├── middleware/
│ │ ├── mod.rs
│ │ └── auth.rs # Authentication middleware
│ └── errors/
│ ├── mod.rs
│ └── app_error.rs # Centralized error types
├── tests/
│ └── integration_tests.rs
└── migrations/
main.rs bootstraps server, loads config, creates pool, wires routesservices/, not in handlersrepositories/ResponseError impl[dependencies]
actix-web = "4"
actix-rt = "2"
actix-cors = "0.7"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sqlx = { version = "0.7", features = ["runtime-tokio", "postgres", "uuid", "chrono"] }
validator = { version = "0.16", features = ["derive"] }
thiserror = "1.0"
anyhow = "1.0"
log = "0.4"
env_logger = "0.10"
tracing = "0.1"
tracing-actix-web = "0.7"
uuid = { version = "1.0", features = ["v4", "serde"] }
chrono = { version = "0.4", features = ["serde"] }
[dev-dependencies]
actix-rt = "2"
use actix_cors::Cors;
use actix_web::{middleware, web, App, HttpServer};
use sqlx::postgres::PgPoolOptions;
pub struct AppState {
pub pool: sqlx::PgPool,
pub config: config::Config,
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
dotenvy::dotenv().ok();
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
let config = config::Config::load().expect("Failed to load configuration");
let pool = PgPoolOptions::new()
.max_connections(10)
.connect(&config.database_url)
.await
.expect("Failed to create database pool");
sqlx::migrate!("./migrations")
.run(&pool)
.await
.expect("Failed to run migrations");
let app_state = web::Data::new(AppState {
pool,
config: config.clone(),
});
HttpServer::new(move || {
let cors = Cors::default()
.allow_any_origin()
.allow_any_method()
.allow_any_header()
.max_age(3600);
App::new()
.app_data(app_state.clone())
.wrap(cors)
.wrap(middleware::Logger::default())
.wrap(middleware::Compress::default())
.configure(routes::configure)
})
.bind(format!("{}:{}", config.host, config.port))?
.run()
.await
}
use actix_web::web;
pub fn configure(cfg: &mut web::ServiceConfig) {
cfg.service(
web::scope("/api/v1")
.route("/health", web::get().to(handlers::health::health_check))
.service(
web::scope("/auth")
.route("/register", web::post().to(handlers::users::register))
.route("/login", web::post().to(handlers::users::login)),
)
.service(
web::scope("/users")
.wrap(crate::middleware::auth::AuthMiddleware)
.route("", web::get().to(handlers::users::list_users))
.route("/{id}", web::get().to(handlers::users::get_user))
.route("/{id}", web::put().to(handlers::users::update_user))
.route("/{id}", web::delete().to(handlers::users::delete_user)),
),
);
}
web::scopeweb::ServiceConfig for modular route registration/api/v1/...)Extractors are how Actix-web injects request data into handler functions.
| Extractor | Purpose | Example |
|-----------|---------|---------|
| web::Json<T> | Deserialize JSON body | body: web::Json<CreateDto> |
| web::Path<T> | URL path parameters | path: web::Path<Uuid> |
| web::Query<T> | Query string parameters | query: web::Query<PaginationQuery> |
| web::Data<T> | Shared application state | state: web::Data<AppState> |
| web::Form<T> | URL-encoded form data | form: web::Form<LoginForm> |
| HttpRequest | Raw request (headers, extensions) | req: HttpRequest |
validator crate with Validate derive)HttpRequestweb::Data for immutable shared state (wrapped in Arc internally).app_data(web::JsonConfig::default().limit(4096))Handlers are async functions that take extractors and return impl Responder.
use actix_web::{web, HttpResponse};
pub async fn register(
state: web::Data<AppState>,
body: web::Json<CreateUserDto>,
) -> AppResult<HttpResponse> {
let service = UserService::new(state.pool.clone(), state.config.clone());
let response = service.register(body.into_inner()).await?;
Ok(HttpResponse::Created().json(response))
}
pub async fn get_user(
state: web::Data<AppState>,
path: web::Path<Uuid>,
) -> AppResult<HttpResponse> {
let service = UserService::new(state.pool.clone(), state.config.clone());
let user = service.get_user(path.into_inner()).await?;
Ok(HttpResponse::Ok().json(user))
}
Result<HttpResponse, AppError> (aliased as AppResult<HttpResponse>).into_inner() to unwrap extractors before passing to servicespub struct AppState {
pub pool: sqlx::PgPool,
pub config: Config,
}
// Register in HttpServer closure:
let app_state = web::Data::new(AppState { pool, config });
App::new().app_data(app_state.clone())
// Access in handlers:
pub async fn handler(state: web::Data<AppState>) -> impl Responder {
let pool = &state.pool;
// use pool...
}
web::Data (internally uses Arc)web::Data in HttpServer::new closure (cheap Arc clone)tokio::sync::RwLock if mutation is requiredAppStateuse actix_web::{http::StatusCode, HttpResponse, ResponseError};
#[derive(Debug)]
pub enum AppError {
NotFound(String),
BadRequest(String),
Unauthorized(String),
Forbidden(String),
Conflict(String),
Validation(String),
Internal(String),
Database(sqlx::Error),
}
impl ResponseError for AppError {
fn error_response(&self) -> HttpResponse {
let (status, message) = match self {
AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg.clone()),
AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
AppError::Unauthorized(msg) => (StatusCode::UNAUTHORIZED, msg.clone()),
AppError::Forbidden(msg) => (StatusCode::FORBIDDEN, msg.clone()),
AppError::Conflict(msg) => (StatusCode::CONFLICT, msg.clone()),
AppError::Validation(msg) => (StatusCode::UNPROCESSABLE_ENTITY, msg.clone()),
AppError::Internal(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Internal error".into()),
AppError::Database(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Database error".into()),
};
HttpResponse::build(status).json(serde_json::json!({
"success": false,
"error": { "code": status.as_u16(), "message": message }
}))
}
}
pub type AppResult<T> = Result<T, AppError>;
ResponseError trait for all custom error typesFrom<T> impls for automatic conversion: sqlx::Error, validator::ValidationErrorsAppResult<T> type alias to reduce boilerplateActix-web middleware uses the Transform + Service traits.
pub struct AuthMiddleware;
impl<S, B> Transform<S, ServiceRequest> for AuthMiddleware
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type Transform = AuthMiddlewareService<S>;
type InitError = ();
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ok(AuthMiddlewareService { service: Rc::new(service) })
}
}
Transform trait for middleware factories, Service for middleware logicreq.extensions_mut().insert(...)).service(web::scope("/protected").wrap(AuthMiddleware))Logger, Compress, NormalizePath, DefaultHeaders.wrap() runs first)use serde::Deserialize;
#[derive(Clone, Deserialize)]
pub struct Config {
pub host: String,
pub port: u16,
pub database_url: String,
pub jwt_secret: String,
pub jwt_expiration_hours: i64,
}
impl Config {
pub fn load() -> anyhow::Result<Self> {
let config = config::Config::builder()
.add_source(config::Environment::default().separator("__").try_parsing(true))
.set_default("host", "127.0.0.1")?
.set_default("port", 8080)?
.build()?;
Ok(config.try_deserialize()?)
}
}
# Development
cargo run # Start server
cargo watch -x run # Watch mode (requires cargo-watch)
RUST_LOG=actix_web=debug cargo run # Debug logging
# Build
cargo build --release # Production build
cargo check # Fast type-check
# Quality
cargo fmt # Format code
cargo clippy -- -D warnings # Lint with deny
cargo test # Run all tests
cargo tarpaulin # Coverage
# Database
sqlx migrate run # Run migrations
sqlx migrate add <name> # Create migration
web::Data for shared application stateweb::scope and ServiceConfigsqlx::PgPool or deadpool)middleware::Compress for response compression.workers(num_cpus::get())validator crateactix-tls)actix-governor or custom middleware)actix-cors with explicit origins in production (not allow_any_origin)actix_web::test module for integration teststest::init_service and shared test stateFor detailed patterns and examples, see:
development
Zig language guardrails, patterns, and best practices for AI-assisted development. Use when working with Zig files (.zig), build.zig, or when the user mentions Zig. Provides comptime patterns, allocator conventions, C interop guidelines, and testing standards specific to this project's coding standards.
tools
WordPress framework guardrails, patterns, and best practices for AI-assisted development. Use when working with WordPress projects, or when the user mentions WordPress. Provides theme development, plugin architecture, REST API, blocks, and security guidelines.
tools
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs. Use when testing web apps, automating browser interactions, or debugging frontend issues.
tools
Suite of tools for creating elaborate, multi-component web applications using modern frontend technologies (React, Tailwind CSS, shadcn/ui). Use for complex projects requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX pages.