Caching

Caching

Blixt provides a Cache with in-memory (default) and Redis backends. Values are serialized via JSON — you work with typed Rust values, not raw bytes.

Using the cache

The cache is always available on AppContext:

use blixt::prelude::*;
use std::time::Duration;

async fn get_user(
    State(ctx): State<AppContext>,
    Path(id): Path<i64>,
) -> Result<impl IntoResponse> {
    let cache_key = format!("user:{id}");

    // try cache first
    if let Some(user) = ctx.cache.get::<User>(&cache_key).await? {
        return render!(UserPage { user });
    }

    // miss — fetch from DB and cache for 5 minutes
    let user = User::find_by_id(&ctx.db, id).await?;
    ctx.cache.set(&cache_key, &user, Duration::from_secs(300)).await?;

    render!(UserPage { user })
}

API

ctx.cache.get::<T>(key).await?        // Option<T> — None on miss
ctx.cache.set(key, &value, ttl).await? // store with TTL
ctx.cache.delete(key).await?           // remove
ctx.cache.exists(key).await?           // bool

Any type implementing Serialize + DeserializeOwned works.

Backends

In-memory (default)

Uses moka for concurrent LRU eviction with per-entry TTL. No configuration needed — works out of the box.

Redis

Enable the redis cargo feature and set environment variables:

# Cargo.toml
blixt = { version = "...", features = ["redis"] }
# .env
CACHE_BACKEND=redis
REDIS_URL=redis://localhost:6379

Configuration

VariableDefaultDescription
CACHE_BACKENDmemorymemory or redis
CACHE_MAX_ENTRIES10000Max entries for in-memory backend
REDIS_URL(required for redis)Redis connection URL
REDIS_POOL_SIZE8Redis connection pool size

Custom backend

Override the default cache on AppContext:

let cache = blixt::cache::from_env()?;
let ctx = AppContext::new(pool, config).with_cache(cache);

Implement CacheBackend for a custom backend:

use blixt::cache::CacheBackend;

struct MyCache;

#[async_trait::async_trait]
impl CacheBackend for MyCache {
    async fn get_bytes(&self, key: &str) -> Result<Option<Vec<u8>>> { ... }
    async fn set_bytes(&self, key: &str, value: &[u8], ttl: Duration) -> Result<()> { ... }
    async fn delete(&self, key: &str) -> Result<()> { ... }
    async fn exists(&self, key: &str) -> Result<bool> { ... }
}