#response #async-trait #handler #config #routing #wildcard #back-end #kurosabi

bin+lib kurosabi

A lightweight asynchronous HTTP server framework built on Tokio

13 releases

Uses new Rust 2024

new 0.3.3 May 14, 2025
0.3.2 May 14, 2025
0.2.9 May 11, 2025
0.1.1 Mar 21, 2025

#6 in #wildcard

Download history 219/week @ 2025-03-19 4/week @ 2025-03-26 1390/week @ 2025-05-07

1,390 downloads per month

MIT license

105KB
2K SLoC

🔥kurosabi🔥

Kurosabi is a blazing fast, lightweight, and simple web framework for Rust, designed to leverage Rust's safety and parallelism. Inspired by the TypeScript framework "hono", Kurosabi aims to provide a productive and enjoyable web development experience.


Features

  • Ultra-lightweight and fast
  • Simple and expressive routing
  • Async handler support
  • Path parameters and wildcards
  • JSON and file responses
  • Custom context support
  • Easy 404 and error handling
  • Fine-grained server configuration

Installation

Add Kurosabi to your Cargo.toml:

[dependencies]
kurosabi = "0.3.0"

Getting Started

1. Define Your Context (Optional)

pub struct MyContext {
    pub name: String,
}
impl MyContext {
    pub fn new(name: String) -> Self {
        MyContext { name }
    }
}

2. Create the Server and Add Routes

use std::{path::PathBuf, sync::Arc};
use kurosabi::{Kurosabi, kurosabi::Context};

#[tokio::main]
async fn main() {
    let arc_context = Arc::new(MyContext::new("Kurosabi".to_string()));
    let mut kurosabi = Kurosabi::with_context(arc_context);

    // Simple text response
    kurosabi.get("/hello", |mut c| async move {
        c.res.text("Hello, World!");
        c
    });

    // Path parameter
    kurosabi.get("/hello/:name", |mut c| async move {
        let name = c.req.path.get_field("name").unwrap_or("World".to_string());
        c.res.text(&format!("Hello, {}!", name));
        c
    });

    // Wildcard
    kurosabi.get("/wild/*", |mut c| async move {
        let path = c.req.path.get_field("*").unwrap_or("unknown".to_string());
        c.res.text(&format!("Wildcard: {}", path));
        c
    });

    // JSON response
    kurosabi.get("/json", |mut c| async move {
        let json_data = r#"{"name": "Kurosabi", "version": "0.1"}"#;
        c.res.json(json_data);
        c
    });

    // File response
    kurosabi.get("/file", |mut c| async move {
        let _ = c.res.file(&c.req, PathBuf::from("README.md"), true).await.unwrap();
        c
    });

    // Form (GET and POST)
    kurosabi.get("/submit", |mut c| async move {
        c.res.html(r#"
        <form action=\"/submit\" method=\"post\">
            <input type=\"text\" name=\"data\" placeholder=\"Enter some data\" />
            <button type=\"submit\">Submit</button>
        </form>
        "#);
        c
    });
    kurosabi.post("/submit", |mut c| async move {
        let body = match c.req.body_form().await {
            Ok(data) => data,
            Err(_) => {
                c.res.set_status(400);
                return c;
            }
        };
        c.res.html(&format!("Received: {:?}", body));
        c
    });

    // 404 handler
    kurosabi.not_found_handler(|mut c| async move {
        let html = format!(
            "<h1>404 Not Found</h1>\n<p>The page you are looking for does not exist.</p>\n<p>debug: {}</p>",
            c.req.header.get_user_agent().unwrap_or("unknown")
        );
        c.res.html(&html);
        c.res.set_status(404);
        c
    });

    // Server configuration
    let mut server = kurosabi.server()
        .host([0, 0, 0, 0])
        .port(8080)
        .thread(8)
        .thread_name("kurosabi-worker".to_string())
        .queue_size(128)
        .build();

    server.run().await;
}

Advanced Features

JSON API with Custom Handler

use kurosabi::api::GETJsonAPI;
use serde::Serialize;

#[derive(Clone)]
pub struct MyAPI;
#[derive(Serialize)]
pub struct ResJsonSchemaVersion {
    pub name: String,
    pub version: String,
}
#[derive(Serialize)]
#[serde(untagged)]
pub enum ResJsonSchema {
    Version(ResJsonSchemaVersion),
    Error(String),
}

#[async_trait::async_trait]
impl GETJsonAPI<Context<Arc<MyContext>>, ResJsonSchema> for MyAPI {
    fn new() -> Self { MyAPI }
    async fn handler(self, c: &mut Context<Arc<MyContext>>) -> ResJsonSchema {
        let name = c.req.path.get_query("name").unwrap_or("Kurosabi".to_string());
        let version = c.req.path.get_query("version").unwrap_or("0.1".to_string());
        ResJsonSchema::Version(ResJsonSchemaVersion { name, version })
    }
}

// Register the API route
kurosabi.get_json_api("/jsonapi", MyAPI::new());

License

MIT

Dependencies

~11–21MB
~430K SLoC