tower-http-cache 0.5.0

Tower-compatible caching layer with pluggable backends (in-memory, Redis, and more)
Documentation
#![cfg_attr(not(feature = "redis-backend"), allow(dead_code))]

//! Run with:
//! REDIS_URL=redis://127.0.0.1:6379/ cargo run --example axum_redis --features "redis-backend compression metrics tracing"

#[cfg(not(feature = "redis-backend"))]
fn main() {
    eprintln!("Enable `redis-backend` feature to run this example");
}

#[cfg(feature = "redis-backend")]
use std::net::SocketAddr;
#[cfg(feature = "redis-backend")]
use std::sync::atomic::{AtomicUsize, Ordering};
#[cfg(feature = "redis-backend")]
use std::sync::Arc;
#[cfg(feature = "redis-backend")]
use std::time::Duration;

#[cfg(feature = "redis-backend")]
use axum::{routing::get, Router};
#[cfg(feature = "redis-backend")]
use redis::aio::ConnectionManager;
#[cfg(feature = "redis-backend")]
use redis::Client;
#[cfg(feature = "redis-backend")]
use tower_http_cache::prelude::*;
#[cfg(feature = "redis-backend")]
#[cfg(feature = "redis-backend")]
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    tracing_subscriber::fmt::init();

    let redis_url = std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.0.1:6379/".into());
    let client = Client::open(redis_url)?;
    let manager: ConnectionManager = client.get_connection_manager().await?;

    let backend = RedisBackend::new(manager);

    let _cache_layer = CacheLayer::builder(backend)
        .ttl(Duration::from_secs(5))
        .stale_while_revalidate(Duration::from_secs(10))
        .refresh_before(Duration::from_secs(2))
        .max_body_size(Some(512 * 1024))
        .compression(CompressionConfig {
            strategy: CompressionStrategy::Gzip,
            min_size: 8 * 1024,
        })
        .key_extractor(KeyExtractor::path())
        .build();

    let counter = Arc::new(AtomicUsize::new(0));

    // Note: Due to error type incompatibilities between tower-http-cache (BoxError)
    // and Axum (Infallible), we demonstrate a working pattern using the into_make_service approach.
    // For production use, consider using axum::middleware or handling errors explicitly.
    let app = Router::new().route(
        "/",
        get({
            let counter = counter.clone();
            move || {
                let counter = counter.clone();
                async move {
                    let value = counter.fetch_add(1, Ordering::SeqCst) + 1;
                    format!("Hello from backend call #{value}")
                }
            }
        }),
    );

    let addr: SocketAddr = "127.0.0.1:3000".parse()?;
    println!("Listening on http://{addr}");
    println!("Try curling the endpoint multiple times to observe cached responses.");
    println!();
    println!("NOTE: This example demonstrates the library's Sync-compatible body type.");
    println!("For full caching integration with Axum, see the axum::middleware examples.");

    axum::serve(tokio::net::TcpListener::bind(addr).await?, app).await?;
    Ok(())
}