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

bin+lib kurosabi

A lightweight asynchronous HTTP server framework built on Tokio

5 unstable releases

Uses new Rust 2024

new 0.3.10 May 18, 2025
0.3.9 May 18, 2025
0.2.9 May 11, 2025
0.1.1 Mar 21, 2025

#338 in Encoding

Download history 219/week @ 2025-03-19 4/week @ 2025-03-26 902/week @ 2025-05-07 1514/week @ 2025-05-14

2,416 downloads per month

MIT license

115KB
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

~12–22MB
~442K SLoC