Hook the Page Cache
Deep dive into JopiJS's HTML caching lifecycle and how to programmatically control, modify, or bypass it.
Overview
The HTML Cache stores the final string of HTML generated from your page.tsx. Because JopiJS uses an Anonymous Page Philosophy, this cache is extremely efficient: it stores one neutral version of the page and serves it to everyone.
However, sometimes you need to intercept this process—to serve different versions based on device type, to bypass the cache for admins, or to inject dynamic markers into the static HTML.
1. Disabling the Cache
By default, every route is cached. You have two ways to disable it:
The Simple Way (Recommended)
Create an empty file named autoCache.disable next to your page.tsx. This tells the engine to skip the caching logic entirely for this route.
The Programmatic Way
If you need conditional disabling, use the config.ts file in your route folder.
import { JopiRouteConfig } from "jopijs";
export default function(ctx: JopiRouteConfig) {
// Completely disable both reading and writing to the cache
ctx.onPage.cache_disableAutomaticCache();
}2. The Cache Lifecycle Hooks
You can hook into different stages of the cache lifecycle via config.ts.
| Hook | Timing | Primary Use Case |
|---|---|---|
cache_beforeCheckingCache | Before the engine looks for a file. | Set sub-caches or bypass based on roles. |
cache_ifNotInCache | Called only if no cache file exists. | Logging "Cache Misses". |
cache_beforeAddToCache | Before writing the generated HTML to disk. | HTML post-processing (cleanup, minification). |
cache_afterGetFromCache | After reading from disk but before sending to user. | Injecting dynamic non-cached markers. |
3. Advanced Use Case: Sub-Caching
A Sub-cache allows you to have multiple cached versions of the same URL. Crucially, sub-caches are parent-aware: if the main page cache is deleted, all its associated sub-caches are automatically cleared.
Example: Mobile vs. Desktop versions
export default function(ctx: JopiRouteConfig) {
ctx.onPage.cache_beforeCheckingCache(async (req) => {
const isMobile = req.headers.get("user-agent")?.includes("Mobile");
if (isMobile) {
// Tell the engine to use/create a specific partition for mobile
const mobileCache = req.cache_getSubCache("mobile_version");
req.cache_useCache(mobileCache);
}
});
}4. Controlled Refresh (Admin Bypass)
You might want administrators to always see a fresh version of the page while serving the cache to regular visitors.
export default function(ctx: JopiRouteConfig) {
ctx.onPage.cache_beforeCheckingCache(async (req) => {
// If an admin visits, ignore the cache and force a re-render
if (req.role_userHasRole("admin")) {
req.cache_ignoreCacheRead();
}
});
}5. Controlling Cache from the UI (page.tsx)
You can also signal the cache engine directly from your React component using useServerEffect. This is useful for one-off bypasses based on component-level logic.
import { useServerEffect, useServerRequest } from "jopijs/ui";
export default function MyPage() {
// Executed only on the server side.
useServerEffect(() => {
const req = useServerRequest();
// Prevent the server from saving THIS specific render result to cache
if (somethingIsWrong) {
req.cache_ignoreCacheWrite();
}
});
return <div>Content...</div>;
}6. HTML Transformation
JopiJS provides powerful tools to modify the HTML string before or after it hits the disk.
Rewriting Before Caching (jQuery-like)
Useful for heavy transformations that should be "baked into" the cache.
export default function(ctx: JopiRouteConfig) {
ctx.onPage.cache_beforeAddToCache(async (req, res) => {
await req.resValue_hookIfHtml(res, (html) => {
// Turn HTML string into a queryable object
const $ = req.jquery_htmlToJquery(html);
// Manipulate the DOM
$(".internal-notes").remove();
$("h1").addClass("premium-title");
return $.html(); // Return the modified string
});
});
}Injecting After Caching
Useful for adding small markers (like debug comments) without polluting the static cache file.
export default function(ctx: JopiRouteConfig) {
ctx.onPage.cache_afterGetFromCache(async (req, res) => {
await req.resValue_hookIfHtml(res, (html) => {
// Fast string concatenation (avoid heavy jQuery here for performance)
return html + `<!-- Served from Cache at ${new Date().toISOString()} -->`;
});
});
}Performance Note: Avoid using req.jquery_htmlToJquery in cache_afterGetFromCache. Since this hook runs on every hit, parsing the DOM will significantly slow down your TTFB.