agentsclimarketplace

Http actix axum

Skill pedromneto97/custom-skills/skills/http-actix-axum

List of custom agent skills

Install
npx -y skills add pedromneto97/custom-skills --skill http-actix-axum

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

HTTP best practices for actix-web 4 and axum 0.7+ Rust backends. Use when: naming REST resources, choosing HTTP status codes, implementing RFC 9457 Problem Details error responses, configuring OWASP security headers, setting up CORS, enabling response compression, versioning APIs, implementing custom request extractors, wiring OpenAPI docs, or structuring the HTTP layer. Covers both actix-web and axum.

SKILL.md

7.5 KB, as published. Nobody here has run it

HTTP Best Practices — actix-web / axum

1. Resource Naming

RuleGoodBad
Plural nouns/orders, /users/order, /getOrders
Lowercase + hyphens/order-items/orderItems, /Order_Items
Hierarchical nesting/orders/{id}/items/order-items?orderId={id}
No verbs in pathPOST /ordersPOST /createOrder
Filter / sort in query/orders?status=pending&sort=created_at/pending-orders

Max nesting depth: 2 levels (/resource/{id}/sub-resource). Avoid deeper hierarchies.


2. API Versioning

Prefix at the router level. Handlers are version-agnostic.

actix-web

// inbound/src/http/router.rs
pub fn configure(cfg: &mut web::ServiceConfig) {
    cfg.service(
        web::scope("/api/v1")
            .service(web::scope("/orders")
                .route("",      web::get().to(list))
                .route("",      web::post().to(create))
                .route("/{id}", web::get().to(get_one))
                .route("/{id}", web::put().to(update))
                .route("/{id}", web::delete().to(delete)),
            ),
    );
}

axum

// inbound/src/http/router.rs
pub fn build_router() -> Router {
    Router::new()
        .nest("/api/v1", Router::new()
            .nest("/orders", Router::new()
                .route("/",    get(list).post(create))
                .route("/:id", get(get_one).put(update).delete(delete)),
            ),
        )
}

3. HTTP Status Codes

OperationMethodSuccessError cases
Fetch oneGET200404 if not found
Fetch listGET200Empty list → 200 [], never 404
CreatePOST201 + Location header400, 422
Full replacePUT200404, 422
Partial updatePATCH200404, 422
DeleteDELETE204 No Content404
Async actionPOST202 Accepted
Bad input400 Bad Request
Unauthenticated401 Unauthorized
Forbidden403 Forbidden
Conflict (duplicate)409 Conflict
Business rule violated422 Unprocessable Entity
Server fault500 Internal Server ErrorNever leak stack traces
// 201 + Location (actix-web)
HttpResponse::Created()
    .insert_header(("Location", format!("/api/v1/orders/{}", order.id)))
    .json(OrderResponse::from(order))

// 201 + Location (axum)
(StatusCode::CREATED, [("Location", format!("/api/v1/orders/{}", order.id))], Json(body))

4. Error Responses — Problem Details (RFC 9457)

→ Read references/problem-details.md for the full struct, ResponseError / IntoResponse impl, From<DomainError>, and validation error mapping.

Quick rules:

  • Content-Type: application/problem+json
  • type is a URI; use "about:blank" when no dedicated error page exists
  • Never expose stack traces, internal IDs, or DB details in detail
  • Convert framework extractor errors (JSON/query/path) into sanitized Problem Details

5. Security Headers (OWASP)

→ Read references/security-headers.md for middleware implementation (actix-web Transform + axum tower-http layer).

Mandatory headers on every response:

HeaderValue
X-Content-Type-Optionsnosniff
X-Frame-OptionsDENY
Referrer-Policystrict-origin-when-cross-origin
Content-Security-Policydefault-src 'self' (tune per app)
Permissions-Policygeolocation=(), microphone=(), camera=()
Strict-Transport-Securitymax-age=31536000; includeSubDomains (HTTPS only)

Remove Server and X-Powered-By response headers.


6. CORS

→ Read references/cors.md for full configuration.

Decision guide:

ScenarioStrategy
Public API, no cookiesallow_any_origin()
Cookie-auth / credentialedExplicit origin allowlist + allow_credentials(true)

Never combine allow_any_origin() with allow_credentials(true) — browsers reject it.

For cookie/session auth, combine CORS policy with cookie security (Secure, HttpOnly, SameSite) and an explicit origin allowlist.


7. Response Compression

→ Read references/compression.md for middleware setup.

  • Priority order: Brotli → gzip → deflate (auto-negotiated from Accept-Encoding)
  • Skip already-compressed content: images (jpeg/png/gif/webp), video, application/zip, application/pdf
  • Skip small responses: < 1 KB gains nothing

8. Input Validation

Use the validator crate to annotate request structs. Call .validate() at the top of each handler; convert ValidationErrors to a 400 Problem Detail via From<ValidationErrors> for ApiError.

→ Read references/problem-details.md for the From<ValidationErrors> impl and the domain-validation bridge pattern.

actix-web

#[derive(Deserialize, Validate)]
#[serde(rename_all = "camelCase")]
pub struct CreateOrderRequest {
    #[validate(length(min = 1, max = 100))]
    pub customer_name: String,
    #[validate(email)]
    pub email: String,
}

pub async fn create_order<R: AppRepository>(
    state: web::Data<AppState<R>>,
    body: web::Json<CreateOrderRequest>,
) -> Result<impl Responder, ApiError> {
    body.validate()?; // ValidationErrors → ApiError via From impl → 400 Problem Detail
    let order = orders::create_order(&state.repo, body.into_inner().into()).await?;
    Ok(HttpResponse::Created()
        .insert_header(("Location", format!("/api/v1/orders/{}", order.id)))
        .json(OrderResponse::from(order)))
}

9. Custom Extractors

Use typed request extractors (FromRequest in actix-web, FromRequestParts in axum) for cross-cutting concerns like auth claims, tenant context, and request-scoped metadata.

→ Read references/extractors.md.


10. OpenAPI / Swagger

Keep handlers transport-focused and annotate route contracts where possible. Expose docs routes conditionally (often only in non-production/debug builds).

→ Read references/openapi.md.

Domain-validation bridge (when domain owns the rules):

use validator::ValidationError;
use domain::use_cases::validate_customer_name; // pure domain fn → Vec<String>

fn customer_name_valid(val: &str) -> Result<(), ValidationError> {
    match validate_customer_name(val) {
        Ok(_) => Ok(()),
        Err(errors) => {
            let mut e = ValidationError::new("customer_name")
                .with_message("Invalid customer name".into());
            e.add_param("errors".into(), &errors);
            Err(e)
        }
    }
}

#[derive(Deserialize, Validate)]
pub struct CreateOrderRequest {
    #[validate(custom(function = "customer_name_valid"))]
    pub customer_name: String,
}

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.