pub fn fragment<T, F>(
ctx: &Ctx,
key: &str,
ttl: Duration,
build: F,
) -> impl Future<Output = Result<Fragment>> + Send + use<T, F>where
T: Template,
F: FnOnce() -> T,Expand description
Fragment caching, like Rails’ <% cache post do %>: the HTML stored under key, or else the template build returns, rendered and stored.
askama templates cannot wait for KV, so the handler caches the costly
part of the page and passes the Fragment to the page template, which
writes it with {{ fragment }} (no |safe needed). key comes from
cache::key with the records’ ids and
updated_at, the locale if translated, and a version to bump when the
template changes (Rails derives it from a template digest; Ocre keeps it
explicit, so a deploy does not rewrite every fragment against the daily
write quota). The KV key is key prefixed with
FRAGMENT_PREFIX. Conditional caching
(cache_if) is an if around the call, rendering the template directly
otherwise.
Failures behave like fetch: KV errors are
logged and the template is rendered; with
STORE_VAR set to "null" it always renders.
The HTML is stored as is (no JSON), and remembered for the rest of the
request.
Free plan: one KV read (100,000 a day), plus one KV write on a miss (1,000 a day). Worth it for fragments that take milliseconds of CPU to render (long lists, Markdown), read far more often than their records change; a cheap fragment costs more quota than the CPU it saves.
§Errors
Error::Internal(500) when theCACHEbinding is missing, naming the fix:ocre g cacheaddsCACHE: bindings.kv(),to cloudflare.config.ts.Error::Internalwhen the KV key is longer than 512 bytes orttlis below 60 seconds.Error::Internalwhen the template fails to render.
§Examples
use std::time::Duration;
use askama::Template;
use axum::{extract::{Path, State}, response::Html};
use ocre::{Ctx, Result, cache::{self, Fragment}, render};
struct Post { id: i64, title: String, updated_at: String }
#[derive(Template)]
#[template(source = "<article><h2>{{ post.title }}</h2></article>", ext = "html")]
struct Card<'a> { post: &'a Post }
#[derive(Template)]
#[template(source = "<main>{{ card }}</main>", ext = "html")]
struct Show { card: Fragment }
async fn show(State(ctx): State<Ctx>, Path(id): Path<i64>) -> Result<Html<String>> {
let post = Post { id, title: "Hello".into(), updated_at: "2026-09-29 14:05:00".into() }; // from D1
let key = cache::key(&[&"posts", &post.id, &post.updated_at, &"card-v1"]);
let card = cache::fragment(&ctx, &key, Duration::from_secs(86_400), || Card { post: &post }).await?;
render(&Show { card })
}