Skip to content
Back to skills

Actix Web

ASecurity

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.

  • 142 stars
  • 0 votes
  • 0 copies
  • 23 views
  • Added May 27, 2026
testing-securityrustgosqltestingapidatabaseperformance

Works with

  • terminal
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned May 27, 2026

npx -y skills add TerminalSkills/skills --skill actix-web --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Actix Web?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Actix Web
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/terminalskills-actix-web/badge)](https://www.skillsdirectory.com/skills/terminalskills-actix-web)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: actix-web
description: |
  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.
license: Apache-2.0
compatibility: 'rust >= 1.72, cargo'
metadata:
  author: terminal-skills
  version: 1.0.0
  category: development
  tags:
    - rust
    - web
    - api
    - async
    - performance
    - type-safe
---

# 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 {
        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")),
    }
}
```

## Testing

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

## Key Patterns

- Use extractors (`web::Json`, `web::Path`, `web::Query`, `web::Data`) for type-safe request parsing
- Implement `ResponseError` on custom error types for automatic HTTP error responses
- Use `web::Data` for shared application state (DB pool, config) — it's cheaply cloneable
- Use `sqlx` with compile-time checked queries (`sqlx::query!`) for production code
- Use `configure` functions to modularize route registration
- Use `#[actix_web::test]` for async integration tests with `test::init_service`

Files in this skill

  • SKILL.md7.7 KB
  • _scores.json1.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.