[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6EE8WDJD0JM2GTVC8D876SC":3,"skill-same:01M6EE8WDJD0JM2GTVC8D876SC":47},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":20,"linkOnly":21,"markdownContent":22,"markdownBody":22,"frontMatter":23,"refName":24,"commitSha":25,"skillDirectory":26,"forksCount":27,"repositoryHtmlUrl":28,"repositoryHomepageUrl":29,"ownerHtmlUrl":30,"ownerBlogUrl":6,"ownerDescription":31,"updatedAt":32,"install":33,"resources":46},"01M6EE8WDJD0JM2GTVC8D876SC","actix-web",null,"ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",66,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002FTerminalSkills\u002Fskills\u002Factix-web\u002FSKILL.md",[],[18,19],"web","actix web","2026-11-10 10:12:30",false,"# Actix Web\n\nActix Web is one of the fastest web frameworks available. It uses Rust's type system for compile-time safety, async\u002Fawait for concurrency, and extractors for ergonomic request handling.\n\n## Installation\n\n```toml\n# Cargo.toml — dependencies\n[dependencies]\nactix-web = \"4\"\nactix-rt = \"2\"\nserde = { version = \"1\", features = [\"derive\"] }\nserde_json = \"1\"\nsqlx = { version = \"0.7\", features = [\"runtime-tokio\", \"postgres\", \"macros\"] }\ntokio = { version = \"1\", features = [\"full\"] }\nenv_logger = \"0.11\"\n```\n\n## Project Structure\n\n```\n# Recommended Actix Web project layout\nsrc\u002F\n├── main.rs              # Entry point and server config\n├── config.rs            # Configuration\n├── routes\u002F\n│   ├── mod.rs           # Route registration\n│   ├── articles.rs      # Article handlers\n│   └── health.rs        # Health check\n├── models\u002F\n│   └── article.rs       # Data structures\n├── db\u002F\n│   └── article.rs       # Database queries\n├── middleware\u002F\n│   └── auth.rs          # Auth middleware\n└── errors.rs            # Error types\n```\n\n## Main and Server Setup\n\n```rust\n\u002F\u002F src\u002Fmain.rs — application entry point\nuse actix_web::{web, App, HttpServer, middleware::Logger};\nuse sqlx::postgres::PgPoolOptions;\n\nmod routes;\nmod models;\nmod db;\nmod errors;\n\n#[actix_web::main]\nasync fn main() -> std::io::Result\u003C()> {\n    env_logger::init();\n\n    let database_url = std::env::var(\"DATABASE_URL\")\n        .unwrap_or_else(|_| \"postgres:\u002F\u002Flocalhost\u002Fmydb\".to_string());\n\n    let pool = PgPoolOptions::new()\n        .max_connections(10)\n        .connect(&database_url)\n        .await\n        .expect(\"Failed to create pool\");\n\n    HttpServer::new(move || {\n        App::new()\n            .wrap(Logger::default())\n            .app_data(web::Data::new(pool.clone()))\n            .configure(routes::configure)\n    })\n    .bind(\"0.0.0.0:8080\")?\n    .run()\n    .await\n}\n```\n\n## Route Configuration\n\n```rust\n\u002F\u002F src\u002Froutes\u002Fmod.rs — centralized route registration\nuse actix_web::web;\n\nmod articles;\nmod health;\n\npub fn configure(cfg: &mut web::ServiceConfig) {\n    cfg.service(\n        web::scope(\"\u002Fapi\")\n            .route(\"\u002Fhealth\", web::get().to(health::check))\n            .service(\n                web::scope(\"\u002Farticles\")\n                    .route(\"\", web::get().to(articles::list))\n                    .route(\"\", web::post().to(articles::create))\n                    .route(\"\u002F{id}\", web::get().to(articles::get))\n                    .route(\"\u002F{id}\", web::delete().to(articles::delete))\n            )\n    );\n}\n```\n\n## Models\n\n```rust\n\u002F\u002F src\u002Fmodels\u002Farticle.rs — data models with serde\nuse serde::{Deserialize, Serialize};\nuse sqlx::FromRow;\n\n#[derive(Debug, Serialize, FromRow)]\npub struct Article {\n    pub id: i32,\n    pub title: String,\n    pub body: String,\n    pub published: bool,\n    pub created_at: chrono::NaiveDateTime,\n}\n\n#[derive(Debug, Deserialize)]\npub struct CreateArticle {\n    pub title: String,\n    pub body: String,\n}\n\n#[derive(Debug, Deserialize)]\npub struct ListParams {\n    pub page: Option\u003Cu32>,\n    pub limit: Option\u003Cu32>,\n}\n```\n\n## Handlers with Extractors\n\n```rust\n\u002F\u002F src\u002Froutes\u002Farticles.rs — request handlers\nuse actix_web::{web, HttpResponse};\nuse sqlx::PgPool;\nuse crate::models::article::{Article, CreateArticle, ListParams};\nuse crate::errors::AppError;\n\npub async fn list(\n    pool: web::Data\u003CPgPool>,\n    query: web::Query\u003CListParams>,\n) -> Result\u003CHttpResponse, AppError> {\n    let limit = query.limit.unwrap_or(20).min(100) as i64;\n    let offset = ((query.page.unwrap_or(1) - 1) * limit as u32) as i64;\n\n    let articles = sqlx::query_as::\u003C_, Article>(\n        \"SELECT * FROM articles WHERE published = true ORDER BY created_at DESC LIMIT $1 OFFSET $2\"\n    )\n    .bind(limit)\n    .bind(offset)\n    .fetch_all(pool.get_ref())\n    .await?;\n\n    Ok(HttpResponse::Ok().json(articles))\n}\n\npub async fn create(\n    pool: web::Data\u003CPgPool>,\n    body: web::Json\u003CCreateArticle>,\n) -> Result\u003CHttpResponse, AppError> {\n    let article = sqlx::query_as::\u003C_, Article>(\n        \"INSERT INTO articles (title, body) VALUES ($1, $2) RETURNING *\"\n    )\n    .bind(&body.title)\n    .bind(&body.body)\n    .fetch_one(pool.get_ref())\n    .await?;\n\n    Ok(HttpResponse::Created().json(article))\n}\n\npub async fn get(\n    pool: web::Data\u003CPgPool>,\n    path: web::Path\u003Ci32>,\n) -> Result\u003CHttpResponse, AppError> {\n    let id = path.into_inner();\n    let article = sqlx::query_as::\u003C_, Article>(\"SELECT * FROM articles WHERE id = $1\")\n        .bind(id)\n        .fetch_optional(pool.get_ref())\n        .await?\n        .ok_or(AppError::NotFound)?;\n\n    Ok(HttpResponse::Ok().json(article))\n}\n```\n\n## Error Handling\n\n```rust\n\u002F\u002F src\u002Ferrors.rs — custom error types\nuse actix_web::{HttpResponse, ResponseError};\nuse std::fmt;\n\n#[derive(Debug)]\npub enum AppError {\n    NotFound,\n    Internal(String),\n    Database(sqlx::Error),\n}\n\nimpl fmt::Display for AppError {\n    fn fmt(&self, f: &mut fmt::Formatter\u003C'_>) -> fmt::Result {\n        match self {\n            AppError::NotFound => write!(f, \"Not found\"),\n            AppError::Internal(msg) => write!(f, \"{}\", msg),\n            AppError::Database(e) => write!(f, \"Database error: {}\", e),\n        }\n    }\n}\n\nimpl ResponseError for AppError {\n    fn error_response(&self) -> HttpResponse {\n        match self {\n            AppError::NotFound => HttpResponse::NotFound().json(serde_json::json!({\"error\": \"not found\"})),\n            _ => HttpResponse::InternalServerError().json(serde_json::json!({\"error\": \"internal error\"})),\n        }\n    }\n}\n\nimpl From\u003Csqlx::Error> for AppError {\n    fn from(e: sqlx::Error) -> Self { AppError::Database(e) }\n}\n```\n\n## Middleware\n\n```rust\n\u002F\u002F src\u002Fmiddleware\u002Fauth.rs — simple auth middleware\nuse actix_web::{dev::ServiceRequest, Error, HttpMessage};\nuse actix_web::error::ErrorUnauthorized;\n\npub async fn validate_token(req: &ServiceRequest) -> Result\u003C(), Error> {\n    let token = req.headers()\n        .get(\"Authorization\")\n        .and_then(|v| v.to_str().ok())\n        .and_then(|v| v.strip_prefix(\"Bearer \"));\n\n    match token {\n        Some(t) => {\n            let user_id = verify_jwt(t).map_err(|_| ErrorUnauthorized(\"invalid token\"))?;\n            req.extensions_mut().insert(user_id);\n            Ok(())\n        }\n        None => Err(ErrorUnauthorized(\"missing token\")),\n    }\n}\n```\n\n## Testing\n\n```rust\n\u002F\u002F tests\u002Farticles_test.rs — integration test\nuse actix_web::{test, App, web};\n\n#[actix_web::test]\nasync fn test_list_articles() {\n    let pool = setup_test_db().await;\n    let app = test::init_service(\n        App::new()\n            .app_data(web::Data::new(pool))\n            .configure(routes::configure)\n    ).await;\n\n    let req = test::TestRequest::get().uri(\"\u002Fapi\u002Farticles\").to_request();\n    let resp = test::call_service(&app, req).await;\n    assert_eq!(resp.status(), 200);\n}\n```\n\n## Key Patterns\n\n- Use extractors (`web::Json`, `web::Path`, `web::Query`, `web::Data`) for type-safe request parsing\n- Implement `ResponseError` on custom error types for automatic HTTP error responses\n- Use `web::Data` for shared application state (DB pool, config) — it's cheaply cloneable\n- Use `sqlx` with compile-time checked queries (`sqlx::query!`) for production code\n- Use `configure` functions to modularize route registration\n- Use `#[actix_web::test]` for async integration tests with `test::init_service`\n",{},"main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002FTerminalSkills\u002Fskills\u002Factix-web",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":7,"skillName":5,"commands":34,"prompt":44,"zipAvailable":45},[35,38,41],{"tool":36,"command":37},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill actix-web",{"tool":39,"command":40},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill actix-web",{"tool":42,"command":43},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill actix-web","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002FTerminalSkills\u002Fskills\u002Factix-web\u002FSKILL.md",true,[],[48,62,72,81,87,94],{"skillNo":49,"name":50,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":51,"categories":52,"keywords":58,"publishedAt":20},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[53],{"categoryNo":54,"categoryName":55,"taxonomyType":56,"taxonomyTypeDesc":57},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[59,60,61],"model","model hierarchy","model hierarchy skill",{"skillNo":63,"name":64,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":65,"categories":66,"keywords":68,"publishedAt":20},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[67],{"categoryNo":54,"categoryName":55,"taxonomyType":56,"taxonomyTypeDesc":57},[69,70,71],"MCP","zoom","zoom mcp",{"skillNo":73,"name":74,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":75,"categories":76,"keywords":77,"publishedAt":20},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[78,79,80,70],"sdk","zoom apps sdk","apps",{"skillNo":82,"name":83,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":84,"categories":85,"keywords":86,"publishedAt":20},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[83],{"skillNo":88,"name":89,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":90,"categories":91,"keywords":93,"publishedAt":20},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[92],{"categoryNo":54,"categoryName":55,"taxonomyType":56,"taxonomyTypeDesc":57},[89],{"skillNo":95,"name":96,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":97,"categories":98,"keywords":99,"publishedAt":20},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[96]]