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:

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.

src/mod_my_feature/@routes/my_page/config.ts
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.

HookTimingPrimary Use Case
cache_beforeCheckingCacheBefore the engine looks for a file.Set sub-caches or bypass based on roles.
cache_ifNotInCacheCalled only if no cache file exists.Logging "Cache Misses".
cache_beforeAddToCacheBefore writing the generated HTML to disk.HTML post-processing (cleanup, minification).
cache_afterGetFromCacheAfter 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

config.ts
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.

config.ts
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.

src/mod_my_feature/@routes/my_page/page.tsx
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.

config.ts
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.

config.ts
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.