Skip to main content

fragment

Function fragment 

Source
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 the CACHE binding is missing, naming the fix: ocre g cache adds CACHE: bindings.kv(), to cloudflare.config.ts.
  • Error::Internal when the KV key is longer than 512 bytes or ttl is below 60 seconds.
  • Error::Internal when 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 })
}