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.