Crate hyperlite

Crate hyperlite 

Source
Expand description

§Hyperlite

Hyperlite is a lightweight HTTP framework built on top of hyper, tokio, and tower for building fast, composable web services without sacrificing control.

§Philosophy

  • Stay close to hyper primitives for maximum flexibility.
  • Embrace Tower’s Service trait for middleware-first composition.
  • Provide type-safe request and response helpers without macros.
  • Favor zero-cost abstractions over hidden magic.

§Key Features

  • Fast path-based routing powered by matchit.
  • Tower middleware compatibility, including tracing, CORS, and authentication layers.
  • Ergonomic response builders and JSON helpers.
  • Built-in server runner with graceful shutdown handling.
  • Focus on predictable upgrades by avoiding breaking framework changes.
  • ✅ Type-safe request extraction helpers for bodies, queries, paths, and extensions.

§Quick Example

use bytes::Bytes;
use hyper::{Method, Request, Response, StatusCode};
use hyperlite::{serve, success, BoxBody, Router};
use http_body_util::Full;
use serde::Serialize;
use std::net::SocketAddr;
use std::sync::Arc;

#[derive(Clone)]
struct AppState;

#[derive(Serialize)]
struct Greeting {
    message: String,
}

async fn hello_handler(
    _req: Request<BoxBody>,
    _state: Arc<AppState>,
) -> Result<Response<Full<Bytes>>, hyperlite::BoxError> {
    Ok(success(
        StatusCode::OK,
        Greeting {
            message: "Hello, World!".to_string(),
        },
    ))
}

#[tokio::main]
async fn main() -> Result<(), hyperlite::BoxError> {
    let state = AppState;
    let router = Router::new(state).route(
        "/hello",
        Method::GET,
        Arc::new(|req, state| Box::pin(hello_handler(req, state))),
    );

    let addr: SocketAddr = "127.0.0.1:3000".parse().unwrap();
    serve(addr, router).await
}

For richer walkthroughs, inspect the examples/ directory:

  • hello_world.rs – Minimal single-route server.
  • with_state.rs – Shared state with all extractors.
  • with_middleware.rs – Complete Tower middleware stack.

Run any example via cargo run --example <name>.

§Request Extraction

use bytes::Bytes;
use hyper::{Method, Request, Response, StatusCode};
use hyperlite::{parse_json_body, path_param, query_params, BoxBody, BoxError, Router, success};
use http_body_util::Full;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use uuid::Uuid;

#[derive(Clone)]
struct AppState;

#[derive(Deserialize)]
struct CreatePost { title: String, body: String }

#[derive(Deserialize)]
struct Pagination { page: u32, limit: u32 }

#[derive(Serialize)]
struct Post { id: Uuid, title: String }

async fn create_post(
    req: Request<BoxBody>,
    _state: Arc<AppState>,
) -> Result<Response<Full<Bytes>>, BoxError> {
    let payload = parse_json_body::<CreatePost>(req).await?;
    Ok(success(StatusCode::CREATED, Post {
        id: Uuid::new_v4(),
        title: payload.title,
    }))
}

async fn list_posts(
    req: Request<BoxBody>,
    _state: Arc<AppState>,
) -> Result<Response<Full<Bytes>>, BoxError> {
    let params = query_params::<Pagination>(&req)?;
    Ok(success(StatusCode::OK, format!("page={}", params.page)))
}

async fn get_post(
    req: Request<BoxBody>,
    _state: Arc<AppState>,
) -> Result<Response<Full<Bytes>>, BoxError> {
    let post_id: Uuid = path_param(&req, "id")?;
    Ok(success(StatusCode::OK, Post { id: post_id, title: "Example".into() }))
}

#[tokio::main]
    let state = AppState;
    let router = Router::new(state)
        .route("/posts", Method::POST, Arc::new(|req, state| Box::pin(create_post(req, state))))
        .route("/posts", Method::GET, Arc::new(|req, state| Box::pin(list_posts(req, state))))
        .route("/posts/{id}", Method::GET, Arc::new(|req, state| Box::pin(get_post(req, state))));

    let addr: std::net::SocketAddr = "127.0.0.1:3000".parse().unwrap();
    hyperlite::serve(addr, router).await
}

See examples/with_state.rs for a full application using these helpers in tandem with shared state and realistic routing patterns.

§Examples

cargo run --example hello_world
cargo run --example with_state
RUST_LOG=info cargo run --example with_middleware

Each example demonstrates progressively more advanced patterns, from simple routing to full middleware stacks with distributed tracing.

§When To Use

Choose Hyperlite when you want the ergonomics of a framework while keeping full control over the underlying HTTP server and middleware stack.

Re-exports§

pub use extract::get_extension;
pub use extract::parse_json_body;
pub use extract::path_param;
pub use extract::path_params;
pub use extract::query_params;
pub use response::empty;
pub use response::failure;
pub use response::not_found;
pub use response::success;
pub use response::with_correlation_id;
pub use response::ApiError;
pub use response::ResponseBody;
pub use router::BodyError;
pub use router::BoxBody;
pub use router::Handler;
pub use router::PathParams;
pub use router::Router;
pub use server::serve;

Modules§

extract
Request extraction helpers for Hyperlite services.
response
Response builders for consistent JSON API envelopes.
router
Usage with Server
server
Server utilities for running Hyperlite applications.

Type Aliases§

BoxError
Boxed error type used across Hyperlite.
Result
Convenience result type used by Hyperlite components.