Composition (route × Suspense × ISR × fetch-cache)

Toutes les couches de cache empilées sur une seule page. L'horodatage extérieur se renouvelle à chaque requête (force-dynamic). Trois frontières Suspense diffusent leur contenu réel au fur et à mesure de la résolution ; chacune enveloppe un fragment <ISR> avec un TTL différent. Le fragment le plus lent effectue en plus un fetch avec next.revalidate, de sorte que la mise en cache en couches maintient le rendu peu coûteux même lors d'un cache miss extérieur.

Astuce
Les couches de cache ne s'annulent pas mutuellement : un miss ISR de 30 s peut encore trouver le résultat de fetch dans le FetchCache (TTL 8 s), ce qui économise l'appel réseau. Ajoutez une balise (tags: ["composition"]) pour invalider sélectivement le FetchCache via /__bext/sdk/cache/purge-tag sans toucher aux fragments ISR.

Résultat (actualisez pour observer les cadences)

Extérieur (en direct) : 2026-06-21T08:40:49.381Z

  • chargement rapide…
  • chargement moyen…
  • chargement lent…

Carte des cadences

CoucheCadenceCache
Extérieurchaque requête
Fragment rapide~5 sFragmentCache (timed)
Fragment moyen~15 sFragmentCache (timed)
Fragment lent~30 sFragmentCache (timed)
Fetch upstream du lent~8 s (seulement lors d'un miss lent)FetchCache + tag "composition"
src/app/examples/composition/page.tsxTSX
import { Suspense } from "@bext-stack/framework/streaming";
import { ISR } from "@bext-stack/framework/cache";

export const dynamic = "force-dynamic";

export default function Page() {
  const outer = new Date().toISOString();
  return (
    <div>
      <p>Outer (live): {outer}</p>
      <Suspense fallback={<p>loading fast…</p>}>
        <ISR cacheKey="composition:fast" ttl={5}>
          {async () => {
            await new Promise(r => setTimeout(r, 200));
            return `<p>Fast (5s ISR): ${new Date().toISOString()}</p>`;
          }}
        </ISR>
      </Suspense>
      <Suspense fallback={<p>loading medium…</p>}>
        <ISR cacheKey="composition:medium" ttl={15}>
          {async () => {
            await new Promise(r => setTimeout(r, 400));
            return `<p>Medium (15s ISR): ${new Date().toISOString()}</p>`;
          }}
        </ISR>
      </Suspense>
      <Suspense fallback={<p>loading slow…</p>}>
        <ISR cacheKey="composition:slow" ttl={30}>
          {async () => {
            await new Promise(r => setTimeout(r, 800));
            // fetch with its own 8s next-cache layer.
            const r = await fetch("https://demo.bext.dev/api/echo?key=composition", {
              next: { revalidate: 8, tags: ["composition"] },
            });
            const data = await r.json();
            return `<p>Slow (30s ISR + 8s fetch-cache): ${data.receivedAt}</p>`;
          }}
        </ISR>
      </Suspense>
    </div>
  );
}
Essayez en directOuvrir dans play

Un projet exécutable de cet exemple, ouvert dans l'éditeur bext play — modifiez le code, l'aperçu se met à jour.

src/app/page.tsx3 fichiers · s'exécute dans votre navigateur
Modifiable en direct — l'aperçu se recompile à chaque frappe.