Working with requests and middleware

Updated Nov 29, 2025

Every inbound frame becomes a ServiceRequest. Middleware can inspect or mutate the byte buffer through frame and frame_mut, adjust correlation identifiers, or wrap the remainder of the pipeline using Transform. The Next continuation calls the inner service, returning a ServiceResponse containing the frame that will be serialized back to the client.[^11]

The middleware::from_fn helper adapts async functions into middleware components, enabling payload decoding, invocation of the inner service, and response editing before the response is serialized. HandlerService rebuilds a packet from the request bytes, invokes the registered handler, and by default returns the original payload; replies are crafted in middleware (or custom packet types with interior mutability). Decode typed payloads via the Message helpers, then write the encoded response into ServiceResponse::frame_mut().[^10][^12]

use std::convert::Infallible;

use wireframe::{
    app::Envelope,
    message::Message,
    middleware::{from_fn, HandlerService, Next, ServiceRequest, ServiceResponse},
};

#[derive(bincode::Encode, bincode::BorrowDecode)]
struct Ping(u8);

#[derive(bincode::Encode, bincode::BorrowDecode)]
struct Pong(u8);

async fn decode_and_respond(
    mut req: ServiceRequest,
    next: Next<'_, HandlerService<Envelope>>,
) -> Result<ServiceResponse, Infallible> {
    let ping = Ping::from_bytes(req.frame()).map(|(msg, _)| msg).ok();
    let mut response = next.call(req).await?;

    if let Some(Ping(value)) = ping {
        let bytes = Pong(value + 1)
            .to_bytes()
            .expect("encode Pong");
        response.frame_mut().clear();
        response.frame_mut().extend_from_slice(&bytes);
    }

    Ok(response)
}

let middleware = from_fn(decode_and_respond);

Advanced integrations can adopt the wireframe::extractor module, which defines MessageRequest, Payload, and FromMessageRequest for building Actix-style extractors in custom middleware or services. These types expose shared state, peer addresses, and payload cursors for frameworks that want to layer additional ergonomics on top of the core primitives.[^13]