Syntax design choose for the multi-protocol web framework

⚓ Rust    📅 2026-01-27    👤 surdeus    👁️ 1      

surdeus

I am working on redesign the syntax for hotaru framework (which is a multi-protocol web framework) as I gathered some feedback from the previous discussion.

Seems most of people don't like the idea of putting all endpoints into one custom macro, like the original design:

endpoint! { 
    APP.url("/new_syntax/<arg>"), 
    middleware: [..],
    config: ["ConfigString"], 

    /// Example endpoint using new syntax 
    pub fn new_syntax_endpoint(context: HTTP) { 
        let args = context.pattern("arg"); 
        text_response(format!("New syntax endpoint called with arg: {}", args.unwrap_or_default())) 
    }
}

I am thinking about the following 2 alternatives:

#[endpoint] 
#[path("/")] 
#[middleware([logger_middleware])]
fn index <HTTP>() { 
    akari_render!(
        "home.html",
        title = "Hotaru Example",
        page_title = "Welcome to Hotaru 0.8",
        show_message = true,
        message = "Framework successfully running!",
        items = [
            "Protocol Abstraction Layer",
            "Async/await support", 
            "Middleware system",
            "Template rendering"
        ]
    ) 
} 

#[middleware] 
fn logger_middleware <HTTP>() {
    println!("Request received: {} {}", req.method(), req.path());
    next(context).await
} 

Or

#[endpoint(path = "/", middleware = [logger_middleware])]
fn index <HTTP>() { 
    akari_render!(
        "home.html",
        title = "Hotaru Example",
        page_title = "Welcome to Hotaru 0.8",
        show_message = true,
        message = "Framework successfully running!",
        items = [
            "Protocol Abstraction Layer",
            "Async/await support", 
            "Middleware system",
            "Template rendering"
        ]
    ) 
} 

#[middleware] 
fn logger_middleware <HTTP>() {
    println!("Request received: {} {}", req.method(), req.path());
    next(context).await
}  

You may specific the APP we register by adding #[app(APP_NAME)] or app=... for both syntax. The APP instance is not specified, in default we will register into the one named as APP as per the convention.

I want to see the community's opinion. Whether those 2 are good enough? And which one we prefer more.

Also, I found a problem with the proc macro (which I am aware when I was designing the old syntax) is that normal proc macro attribute only accepts normal rust syntax. Which means the following is not accepted:

#[middleware] 
fn logger_middleware <HTTP>(req) {
    println!("Request received: {} {}", req.method(), req.path());
    next(context).await
}  

Then if we want to give some other names for the req we must do the following:

#[middleware] 
fn logger_middleware <HTTP>(req_name: DummyType) {
    println!("Request received: {} {}", req_name.method(), req_name.path());
    next(context).await
}  

I am thinking how to deal with this or we just simply ban custom naming for the request variable?

Previous discussion: We built a Rust web framework around one idea: everything about an endpoint belongs together. Feedback wanted

Repo: GitHub - Field-of-Dreams-Studio/hotaru: Small, sweet, easy framework for full-stack Rust web applications supporing multiple & user-defined protocol (Please refer to 0.8-Phase 1 branch instead of master branch)

1 post - 1 participant

Read full topic

🏷️ Rust_feed