YAHF

Yet Another HTTP Framework

> **Warning: YAHF works only on nightly until [`RPITIT`](https://releases.rs/docs/1.75.0/) is stable** The goal of `YAHF` is both to provide a good developer experience and to be easy to extend. # Table of Contents - [Features](#features) - [Example](#example) - [Routing](#routing) - [Handlers](#handlers) - [Extensability](#extensability) - [Middleware](#middleware) - [Examples](#examples) - [Goals](#goals-for-v1.0.0) # Features - Macro free Routing API - Predictable error handling - Native serialization and deserialization built into the handler - Friendly syntax # Example The `Hello world` of YAHF is: ```rust use yahf::server::Server; #[tokio::main] async fn main() { let server = Server::new().get( "/", || async { "Hello world".to_string() }, &(), &String::with_capacity(0), ); server .listen(([127, 0, 0, 1], 8000).into()) .await .unwrap(); } ``` # Routing [`Router`](router::Router) is used to bind handlers to paths.\ ```rust use yahf::router::Router; // Router let router = Router::new() .get("/", root_get, &(), &()) .get("/foo", foo_get, &(), &()) .post("/foo", foo_post, &(), &()) .delete("/foo/bar", bar_delete, &(), &()); // calls respectively each of these handlers async fn root_get() {} async fn foo_get() {} async fn foo_post() {} async fn bar_delete() {} ``` [`Server`](server::Server) shares these features from [`Router`](router::Router) # Handlers On YAHF, a [`handler`](handler) is a async function that is used to handle a `Route`. An acceptable `handler` implements the trait [`Runner`](handler::Runner). By default, these signatures are supported: ```rust async fn handler1() -> ResponseBody {todo!()} async fn handler2() -> Response {todo!()} async fn handler3(req: RequestBody) -> ResponseBody {todo!()} async fn handler4(req: Request) -> ResponseBody {todo!()} async fn handler5(req: RequestBody) -> Response {todo!()} async fn handler6(req: Request) -> Response {todo!()} async fn handler7() -> Result {todo!()} async fn handler8() -> Result> {todo!()} async fn handler9(req: Result) -> Result {todo!()} async fn handler10(req: Result>) -> Result {todo!()} async fn handler11(req: Result) -> Result> {todo!()} async fn handler12(req: Result>) -> Result> {todo!()} ``` All these signatures comes from the implementations of [`RunnerInput`](runner_input::RunnerInput) and [`RunnerOutput`](runner_output::RunnerOutput). # Extensability YAHF `handlers` are modular by design. A `handler` is decomposed into four modules: a body [`deserializer`](deserializer::BodyDeserializer), a body [`serializer`](serializer::BodySerializer), [`arguments`](runner_input::RunnerInput), and a [`response`](runner_output::RunnerOutput). These modules are glued together using the [`Runner`](handler::Runner) trait. Adding new functionality to the handlers is just a matter of implementing one of these traits. For more details, check the trait docs # Middleware [`Middleware`](middleware) are async functions that will run previously or after a `handler`. These can really useful when combined with a [`Router`](router::Router) or a [`Server`](server::Server) to reuse logic and create `"pipelines"`.
Example ```rust use serde::Deserialize; use serde::Serialize; use yahf::handler::Json; use yahf::request::Request; use yahf::result::Result; use yahf::response::Response; use yahf::router::Router; use yahf::server::Server; use std::time; use std::time::UNIX_EPOCH; #[derive(Debug, Deserialize, Serialize)] struct ComputationBody { value: u32, } // Print the time, the method, and the path from the Request async fn log_middleware(req: Result>) -> Result> { match req.into_inner() { Ok(req) => { println!( "{} - {} - {}", time::SystemTime::now() .duration_since(UNIX_EPOCH) .expect("Negative time") .as_millis(), req.method().as_str(), req.uri().path() ); Ok(req).into() } Err(err) => Err(err).into(), } } // Handle any possible errors async fn log_error(res: Result>) -> Result> { match res.into_inner() { Err(err) => { println!( "{} - {}", time::SystemTime::now() .duration_since(UNIX_EPOCH) .expect("Negative time") .as_millis(), err.code(), ); Err(err).into() } ok => ok.into(), } } // Compute something using the ComputationBody async fn some_computation(req: ComputationBody) -> ComputationBody { ComputationBody { value: req.value + 1, } } // Set a [`Router`](router::Router) with both `Middlewares`. // The route `/` will become: `log_middleware -> some_computation -> log_middleware` let router = Router::new() .pre(log_middleware) .after(log_error) .get("/", some_computation, &Json::new(), &Json::new()); ``` More of this example [here](https://github.com/lucasduartesobreira/yahf/blob/main/examples/router_example/main.rs)
# Examples The repo includes [illustrative examples](https://github.com/lucasduartesobreira/yahf/tree/main/examples) demonstrating the integration of all the components ## Goals for v1.0.0 > **`YAHF` follows the `SemVer`.** The objective for v1.0.0 is to have a stable project that can deal with real-world problems with good developer experience and the possibility to extend the project to suit any need. The goal features for this version are: - [x] Composable routing system; - [x] Middleware functions; - [x] HTTP/1.1 with or without security.