What is the difference between http::Request<hyper::Body> and http::Request<axum::body::Body> in handler functions?

http::Request<hyper::Body> and http::Request<axum::body::Body> represent the same underlying concept—an HTTP request with a body—but come from different versions of the ecosystem. In hyper 0.14 and earlier, hyper::Body was a concrete type implementing http_body::Body directly. In hyper 1.0 and axum 0.7+, axum::body::Body is a type alias for http_body::Body<Bytes> or the crate's re-export that abstracts over different body implementations. The key difference is that axum::body::Body supports both hyper 1.0's new body model and provides additional extraction and middleware integration, while hyper::Body (from older versions) was tightly coupled to hyper's older async runtime model. When writing axum handlers, use axum::body::Body for compatibility with axum's extractor ecosystem, while hyper::Body appears in legacy code or when interfacing directly with hyper 0.14.

Body Types in the HTTP Ecosystem

// hyper 0.14 (legacy)
use hyper::Body;  // Concrete type specific to hyper
 
// axum 0.6 and earlier (built on hyper 0.14)
use axum::body::Body;  // Re-export of hyper::Body
 
// hyper 1.0+ and axum 0.7+
use axum::body::Body;  // Abstracts over http_body::Body implementations
use http_body::Body;   // Trait for body implementations
 
// The http crate is agnostic
use http::Request;  // Generic over any body type

The http::Request<B> type is generic over any body type B.

hyper::Body in Hyper 0.14

// Legacy hyper 0.14 style
use hyper::{Body, Request, Response};
use hyper::service::{make_service_fn, service_fn};
 
async fn handle(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
    // Body is a concrete type specific to hyper 0.14
    let body_bytes = hyper::body::to_bytes(req.into_body()).await?;
    
    Ok(Response::new(Body::from("Hello")))
}
 
#[tokio::main]
async fn main() {
    // hyper 0.14 server setup
    let make_svc = make_service_fn(|_conn| {
        async { Ok::<_, hyper::Error>(service_fn(handle)) }
    });
    
    // Server uses hyper::Body directly
}

hyper::Body in hyper 0.14 was a concrete type with stream-based body handling.

axum::body::Body in Axum 0.7+

// Modern axum 0.7+ style (hyper 1.0)
use axum::{
    body::Body,
    extract::Request,
    response::Response,
    routing::get,
    Router,
};
 
async fn handle(req: Request) -> Response<Body> {
    // Body abstracts over different body implementations
    Response::new(Body::from("Hello"))
}
 
#[tokio::main]
async fn main() {
    let app = Router::new().route("/", get(handle));
    
    // Axum handles body type conversion internally
    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

axum::body::Body in axum 0.7+ works with hyper 1.0's body model.

Request Body Type Differences

// In axum 0.6 (hyper 0.14):
// axum::body::Body is an alias for hyper::Body
// http::Request<hyper::Body> is the request type
 
// In axum 0.7+ (hyper 1.0):
// axum::body::Body wraps different underlying types
// http::Request<axum::body::Body> is the request type
 
use http::Request;
 
// The Request type is the same, body type differs
fn process_request_v06(req: Request<hyper::Body>) {
    // hyper 0.14 body
}
 
fn process_request_v07(req: Request<axum::body::Body>) {
    // axum 0.7+ body (hyper 1.0 compatible)
}

The http::Request type is shared; the body type parameter differs.

Body as Bytes

use axum::{
    body::Body,
    extract::Request,
};
use http_body_util::BodyExt;
 
async fn get_body_bytes(req: Request) -> Vec<u8> {
    // Collect body into bytes
    let body = req.into_body();
    let bytes = body.collect().await.unwrap().to_bytes();
    bytes.to_vec()
}
 
// Alternative using axum's Bytes extractor
use axum::body::Bytes;
 
async fn extract_bytes(body: Bytes) -> Vec<u8> {
    body.to_vec()
}

Both versions support collecting body to bytes.

Streaming Body Content

use axum::body::Body;
use axum::extract::Request;
use http_body_util::BodyExt;
use futures::StreamExt;
 
async fn stream_body(req: Request) {
    let mut body = req.into_body();
    
    // Using http_body_util for streaming
    while let Some(frame) = body.frame().await.transpose().unwrap() {
        if let Some(data) = frame.data_ref() {
            println!("Received {} bytes", data.len());
        }
    }
}
 
// In hyper 0.14:
async fn stream_legacy(req: hyper::Request<hyper::Body>) {
    let mut body = req.into_body();
    
    while let Some(chunk) = body.next().await {
        let chunk = chunk.unwrap();
        println!("Received {} bytes", chunk.len());
    }
}

Body streaming differs between hyper 0.14 and hyper 1.0+.

Handler Function Signatures

use axum::{
    body::Body,
    extract::Request,
    response::{Response, IntoResponse},
};
 
// Modern axum 0.7+ handler
async fn handler_v07(req: Request) -> Response<Body> {
    // Request is http::Request<axum::body::Body>
    Response::new(Body::from("Response"))
}
 
// Legacy axum 0.6 handler
async fn handler_v06(req: hyper::Request<hyper::Body>) -> hyper::Response<hyper::Body> {
    // Request is http::Request<hyper::Body>
    hyper::Response::new(hyper::Body::from("Response"))
}
 
// Using IntoResponse (works in both)
async fn handler_into_response() -> impl IntoResponse {
    // IntoResponse handles body conversion
    "Response"
}

Modern axum uses axum::body::Body; legacy uses hyper::Body.

Body Type Conversions

use axum::body::Body;
use http_body_util::BodyExt;
 
// Convert between body types
async fn convert_body(body: Body) -> Vec<u8> {
    // Body -> Bytes
    let bytes = body.collect().await.unwrap().to_bytes();
    bytes.to_vec()
}
 
// Create body from various sources
fn create_bodies() {
    // From bytes
    let body1 = Body::from("Hello");
    
    // From vec
    let body2 = Body::from(vec![1, 2, 3]);
    
    // From string
    let body3 = Body::from(String::from("World"));
    
    // Empty body
    let body4 = Body::empty();
}

Body::from accepts various types that implement Into<Body>.

Extracting Body in Axum

use axum::{
    body::{Body, Bytes},
    extract::Request,
    response::Response,
};
 
// Method 1: Extract whole body
async fn extract_body_bytes(body: Bytes) -> Vec<u8> {
    body.to_vec()
}
 
// Method 2: Extract request and get body
async fn extract_request_body(req: Request) -> Response {
    let body = req.into_body();
    // Process body...
    Response::new(Body::from("OK"))
}
 
// Method 3: Use Body extractor directly
async fn extract_body(body: Body) -> Response {
    // Body implements IntoResponse
    Response::new(body)
}

Axum provides multiple ways to access request body content.

Response Body Types

use axum::{
    body::Body,
    response::{Response, IntoResponse},
};
use http_body_util::Full;
use bytes::Bytes;
 
async fn create_responses() {
    // Simple string body
    let response1 = Response::new(Body::from("Hello"));
    
    // Bytes body
    let response2 = Response::new(Body::from(Bytes::from("Hello")));
    
    // Using IntoResponse
    let response3 = "Hello".into_response();
    
    // JSON response (with axum::Json)
    let response4 = axum::Json(serde_json::json!({"message": "hello"})).into_response();
}

Multiple types can be converted to response bodies.

Integration with Hyper 1.0

use axum::body::Body;
use http_body_util::BodyExt;
 
// In hyper 1.0, bodies implement http_body::Body trait
// axum::body::Body wraps this trait
 
async fn use_with_hyper1(body: Body) {
    // Body implements http_body::Body
    let bytes = body.collect().await.unwrap().to_bytes();
    
    // Can convert to different body types
    let body2 = Body::from(bytes);
}

axum::body::Body works with hyper 1.0's http_body::Body trait.

Legacy Hyper 0.14 Integration

// In hyper 0.14, hyper::Body was the concrete type
// It implemented Stream<Item = Result<Bytes, Error>>
 
async fn use_with_hyper014(body: hyper::Body) {
    use hyper::body::to_bytes;
    
    // Collect to bytes
    let bytes = to_bytes(body).await.unwrap();
}
 
// The difference:
// hyper 0.14: hyper::Body implements Stream
// hyper 1.0+: Body implements http_body::Body trait

Hyper 0.14 used Stream; hyper 1.0+ uses the http_body::Body trait.

Type Aliases and Re-exports

// axum 0.6 and earlier
// pub type Body = hyper::Body;
 
// axum 0.7+
// pub type Body = <...>;  // Abstract body type
 
use axum::body::Body;
 
// Always use axum::body::Body in axum handlers
// The type alias ensures compatibility with axum version
 
// In generic code, use http_body::Body trait
use http_body::Body as HttpBody;
 
async fn generic_handler<B>(req: http::Request<B>)
where
    B: HttpBody,
{
    // Work with any body type
}

Use axum::body::Body for handlers; use http_body::Body trait for generic code.

Middleware Body Handling

use axum::{
    body::Body,
    extract::Request,
    middleware::Next,
    response::Response,
};
 
async fn body_middleware(req: Request, next: Next) -> Response {
    // Read body
    let (parts, body) = req.into_parts();
    let bytes = body.collect().await.unwrap().to_bytes();
    
    println!("Body size: {}", bytes.len());
    
    // Reconstruct request
    let req = Request::from_parts(parts, Body::from(bytes));
    
    next.run(req).await
}

Middleware can read and reconstruct bodies using Body::from.

Error Handling

use axum::{
    body::Body,
    extract::Request,
    response::Response,
};
use http_body_util::BodyExt;
 
async fn handle_body_errors(req: Request) -> Result<Response, Box<dyn std::error::Error>> {
    let body = req.into_body();
    
    // Body operations can fail
    match body.collect().await {
        Ok(collected) => {
            let bytes = collected.to_bytes();
            Ok(Response::new(Body::from(bytes)))
        }
        Err(e) => {
            // Handle body collection error
            Err(e.into())
        }
    }
}

Body operations return Result with error handling.

Body Trait Bounds

use axum::body::Body;
use http_body::Body as HttpBody;
 
// Body implements HttpBody
fn require_body_trait(body: Body) -> impl HttpBody {
    body
}
 
// Common trait bounds
use http_body_util::BodyExt;
 
async fn with_bounds<B>(body: B) -> Vec<u8>
where
    B: HttpBody + Send + 'static,
    B::Error: std::error::Error + Send + Sync,
{
    body.collect().await.unwrap().to_bytes().to_vec()
}

axum::body::Body implements http_body::Body with necessary bounds.

Performance Considerations

use axum::body::Body;
use axum::extract::Request;
use http_body_util::BodyExt;
 
async fn efficient_body_handling(req: Request) {
    // If you need the whole body, collect once
    let bytes = req.into_body().collect().await.unwrap().to_bytes();
    
    // Reuse the bytes
    let body = Body::from(bytes.clone());
    
    // Avoid: Multiple collections of the same body
    // Bodies can only be consumed once
}
 
// For streaming, avoid buffering entire body
async fn stream_efficiently(req: Request) {
    use futures::StreamExt;
    
    // Process chunks as they arrive
    let mut body = req.into_body();
    while let Some(frame) = body.frame().await.transpose().unwrap() {
        if let Some(data) = frame.data_ref() {
            // Process data immediately
        }
    }
}

Bodies can only be consumed once; avoid unnecessary buffering.

Migration from Hyper 0.14

// Before (hyper 0.14 / axum 0.6):
use hyper::{Body, Request, Response};
 
async fn old_handler(req: Request<Body>) -> Response<Body> {
    let bytes = hyper::body::to_bytes(req.into_body()).await.unwrap();
    Response::new(Body::from(bytes))
}
 
// After (hyper 1.0 / axum 0.7+):
use axum::body::Body;
use axum::extract::Request;
use axum::response::Response;
use http_body_util::BodyExt;
 
async fn new_handler(req: Request) -> Response {
    let bytes = req.into_body().collect().await.unwrap().to_bytes();
    Response::new(Body::from(bytes))
}

Key migration changes: hyper::body::to_bytes becomes body.collect().await.to_bytes().

Compatibility Summary

// Hyper 0.14 / Axum 0.6:
// - hyper::Body is the concrete type
// - hyper::body::to_bytes() for collecting
// - hyper::Request<hyper::Body> for requests
// - hyper::Response<hyper::Body> for responses
 
// Hyper 1.0 / Axum 0.7+:
// - axum::body::Body is the abstract type
// - body.collect().await for collecting
// - axum::extract::Request for requests (wraps http::Request)
// - axum::response::Response for responses (wraps http::Response)
// - http_body::Body trait for generic handling

Migration requires updating body handling APIs.

Synthesis

hyper::Body (hyper 0.14):

  • Concrete type specific to hyper 0.14
  • Implements Stream<Item = Result<Bytes, Error>>
  • Collected via hyper::body::to_bytes()
  • Used in axum 0.6 and earlier
  • Directly tied to hyper's runtime

axum::body::Body (axum 0.7+):

  • Abstracts over body implementations
  • Implements http_body::Body trait
  • Collected via body.collect().await
  • Used in axum 0.7+ with hyper 1.0
  • Works with http_body_util combinators

Key differences:

  • Type: hyper::Body is concrete; axum::body::Body abstracts over implementations
  • Trait: Old uses Stream; new uses http_body::Body
  • Collection: Old uses hyper::body::to_bytes; new uses body.collect()
  • Ecosystem: Old is hyper 0.14; new is hyper 1.0+ compatible

When to use each:

  • Use axum::body::Body in axum 0.7+ handlers
  • Use hyper::Body only for legacy hyper 0.14 code
  • Use http_body::Body trait for generic body handling
  • Prefer axum extractors (Bytes, String, Json) over raw body access

Migration path:

  • Replace hyper::Body with axum::body::Body
  • Replace hyper::body::to_bytes() with body.collect().await?.to_bytes()
  • Replace hyper::Request with axum::extract::Request
  • Replace hyper::Response with axum::response::Response