Skip to main content
ClaudeWave
Skill119 estrellas del repoactualizado 2d ago

actix-web

Actix Web is a high-performance Rust web framework that leverages compile-time type safety and async/await for building concurrent web applications. Use it when developing REST APIs or web services requiring strong performance, type safety, and ergonomic request handling through extractors, particularly for projects needing PostgreSQL database integration and middleware support like authentication and logging.

Instalar en Claude Code
Copiar
git clone --depth 1 https://github.com/TerminalSkills/skills /tmp/actix-web && cp -r /tmp/actix-web/skills/actix-web ~/.claude/skills/actix-web
Después abre una sesión nueva de Claude Code; el skill carga automáticamente.

SKILL.md

# Actix Web

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.

## Installation

```toml
# 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"
```

## Project Structure

```
# 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
```

## Main and Server Setup

```rust
// 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
}
```

## Route Configuration

```rust
// 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))
            )
    );
}
```

## Models

```rust
// 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>,
}
```

## Handlers with Extractors

```rust
// 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))
}
```

## Error Handling

```rust
// 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) }
}
```

## Middleware

```rust
// 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 {