kasi-cli 0.3.0

CLI project scaffolder for the Kasi async web framework.
kasi-cli-0.3.0 is not a library.

Kasi (kasi-jb)

Kasi is an async, macro-driven web and microservice framework for Rust built on Hyper 1.x and Tokio. It combines compile-time route and schema generation with sequential request extractors, RFC 7807 error responses, OpenID Connect JWT verification, static file serving, multipart uploads, and zero-boilerplate OpenAPI 3.1 documentation.


Installation & CLI Scaffolder

Scaffold a New Project with kasi-cli

Install the CLI scaffolder and generate a ready-to-run service in seconds:

cargo install kasi-cli
kasi new my-service
cd my-service
cargo run

Add kasi-jb to an Existing Project

cargo add kasi-jb

Enable optional feature flags as needed in Cargo.toml:

[dependencies]
# Default enables both `postgres` (SeaORM + SQLx) and `templates` (MiniJinja)
kasi-jb = "0.3.0"

# Or opt into only what you need:
# kasi-jb = { version = "0.3.0", default-features = false, features = ["templates"] }

Quick Start

use kasi_jb::prelude::*;

#[endpoint(GET, "/hello/{name}")]
async fn greet(Path(name): Path<String>) -> String {
    format!("Hello, {}!", name)
}

#[main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let app = Application::builder(Config {
        host: "127.0.0.1".into(),
        port: 8080,
    })
    .middleware(TracingMiddleware::new())
    .route(greet)
    .build()
    .await?;

    // Listens on http://127.0.0.1:8080 and drains connections gracefully on Ctrl+C
    app.serve().await?;
    Ok(())
}

Core Features

1. Declarative Routing & Regex Path Constraints

Use #[endpoint(METHOD, "/path")] to define async handlers and mount them directly with .route(fn_name). Endpoints can return Result<T, E> or any IntoResponse type directly (String, &'static str, JsonValue, Html). Path parameters support inline regular expressions and deserialize into primitives or structs via Path<T>.

#[endpoint(GET, "/items/{sku:[A-Z]{3}-\\d+}")]
async fn get_item(Path(sku): Path<String>) -> JsonValue {
    json!({ "sku": sku, "in_stock": true })
}

2. Sequential Request Extractors & Validation

Extractors consume and thread request ownership sequentially, allowing multiple extractors per handler without borrow or move conflicts:

  • Path<T>: Extracts URL path parameters.
  • Query<T>: Deserializes URL query strings.
  • Json<T>: Deserializes JSON request bodies.
  • ValidatedJson<T>: Deserializes JSON and runs #[derive(Validate)] rules automatically, returning an RFC 7807 400 Bad Request on failure.
  • Multipart: Parses multipart/form-data text fields and binary file uploads (UploadedFile).
  • Cookies: Extracts parsed Cookie headers into a key-value map.
  • Extension<T>: Clones shared application state registered via .extension(...).
  • RequestContext: Provides the request URI, HTTP method, and active trace_id.
#[derive(Deserialize, Validate)]
struct CreateUser {
    #[validate(min_len = 3, regex = "^[a-z0-9_]+\$")]
    username: String,
    #[validate(email)]
    email: String,
    #[validate(range(min = 18, max = 120))]
    age: u8,
}

#[endpoint(POST, "/users")]
async fn create_user(
    ValidatedJson(user): ValidatedJson<CreateUser>,
) -> Result<JsonValue, Error> {
    Ok(json!({ "created": user.username }))
}

3. Multipart File Uploads

Extract text fields and uploaded files directly with Multipart:

#[endpoint(POST, "/upload")]
async fn upload_file(form: Multipart) -> JsonValue {
    let title = form.fields.get("title").cloned().unwrap_or_default();
    let count = form.files.len();
    json!({ "title": title, "uploaded_files": count })
}

4. Static File Serving & MiniJinja Templates

Serve static assets (CSS, JS, images) safely with path-traversal protection via .serve_dir(url_prefix, dir), and render server-side HTML templates with TemplateEngine (feature = "templates").

#[endpoint(GET, "/dashboard/{user}")]
async fn dashboard(
    engine: TemplateEngine,
    Path(user): Path<String>,
) -> Result<Html, Error> {
    engine.render("dashboard.html", template_context!(user => user))
}

// Mount static files and templates on ApplicationBuilder:
// app.extension(TemplateEngine::from_dir("templates"))
//    .serve_dir("/static", "public")

5. Cookie & Session Management

Read incoming browser cookies via Cookies and issue HttpOnly / SameSite cookies using Cookie:

#[endpoint(POST, "/session")]
async fn set_session() -> Response<BoxBody> {
    let mut res = "Session active".into_response();
    Cookie::new("sid", "session_token_123")
        .http_only(true)
        .same_site(SameSite::Strict)
        .max_age(3600)
        .append_to_headers(res.headers_mut());
    res
}

#[endpoint(GET, "/session")]
async fn read_session(cookies: Cookies) -> JsonValue {
    json!({ "sid": cookies.get("sid") })
}

6. Polymorphic Responses & RFC 7807 Problem Details

Derive IntoResponse on structs for 200 OK JSON responses, and on enums for standardized application/problem+json error envelopes.

#[derive(Serialize, IntoResponse, Schema)]
struct UserDto {
    id: uuid::Uuid,
    username: String,
}

#[derive(IntoResponse)]
enum ApiError {
    #[problem(status = 404, title = "User Not Found")]
    NotFound,
    #[problem(status = 403, title = "Insufficient Permissions")]
    Forbidden,
}

7. Auto-Generated OpenAPI 3.1 Specs & Swagger UI

Annotate endpoints with #[response(...)] and derive Schema on your models. Kasi builds the OpenAPI 3.1.0 specification at startup and serves it automatically:

  • OpenAPI JSON: GET /openapi.json
  • Interactive Swagger UI: GET /docs
#[endpoint(GET, "/users/{id}")]
#[response(status = 200, description = "Fetched user record", json = UserDto)]
#[response(status = 404, description = "User does not exist")]
async fn fetch_user(Path(id): Path<uuid::Uuid>) -> Result<UserDto, ApiError> {
    Err(ApiError::NotFound)
}

// Register schemas on ApplicationBuilder:
// app.register_schema::<UserDto>().route(fetch_user)

8. OpenID Connect (OIDC) JWT Security

Protect routes declaratively with #[security(...)]. OidcConfig::discover fetches and caches your identity provider's JWKS (Keycloak, Auth0, Okta, Google) at startup for fast local signature verification.

#[endpoint(DELETE, "/admin/users/{id}")]
#[security(RequireRole = "Administrator", OAuth2 = ["users:delete"])]
async fn delete_user(
    auth: AuthContext,
    Path(id): Path<uuid::Uuid>,
) -> Result<&'static str, Error> {
    println!("Deleted {} by admin {}", id, auth.email);
    Ok("Deleted")
}

9. Database Pools & Automatic Transactions (feature = "postgres")

Inject SeaORM (Db) or SQLx (DbPool) connections via .extension(...), or use the Tx extractor to open a SeaORM transaction that commits automatically on Ok and rolls back on Err.

#[endpoint(POST, "/orders")]
async fn place_order(
    mut tx: Tx,
    Json(order): Json<NewOrder>,
) -> Result<&'static str, Error> {
    // Queries executed on `&tx.0` automatically commit on Ok or roll back on Err
    Ok("Order committed")
}

10. Modular Route Nesting & Built-In Middleware

Group endpoints under URL prefixes with isolated middleware using Module, and attach global middleware layers to ApplicationBuilder:

  • TracingMiddleware::new() / TracingMiddleware::json(): Propagates X-Request-Id headers and logs request latency.
  • RateLimitMiddleware::new(max_requests, window): Fixed-window per-client throttling with X-RateLimit-* headers and 429 responses.
  • CorsMiddleware: Configurable cross-origin resource sharing.
let inventory = Module::new("/inventory").route(get_item);

let api_v1 = Module::new("/api/v1")
    .wrap(from_fn(|req, next| async move { Ok(next.run(req).await) }))
    .nest_with_docs(inventory);

let app = Application::builder(Config { host: "127.0.0.1".into(), port: 8080 })
    .middleware(TracingMiddleware::json())
    .middleware(RateLimitMiddleware::new(100, std::time::Duration::from_secs(60)))
    .middleware(CorsMiddleware::permissive())
    .register_module(api_v1)
    .build()
    .await?;

Workspace Crates

Crate Purpose
kasi-jb Umbrella facade and prelude re-exporting the full framework API.
kasi-core Hyper runtime, regex router, extractors, static files, cookies, middleware, templates, and OpenAPI generator.
kasi-macros Procedural macros (#[endpoint], #[main], #[derive(IntoResponse, Validate, Schema)]).
kasi-security OIDC discovery client, JWKS caching, and AuthContext RBAC/scope guards.
kasi-cli Command-line project scaffolder (kasi new <project-name>).

License

MIT License — Copyright (c) 2026 Justice Sam.