Advanced Configuration
Customize the cache behavior using the config.ts file and JopiRouteConfig hooks.
Overview
While the JopiJS cache works out-of-the-box with sensible defaults (enabled for everyone, "autoCache.disable" to turn it off completely), real-world applications often require more granular control.
The config.ts file in your route folder allows you to programmatically configure the cache behavior using the JopiRouteConfig object.
Accessing the Configuration
To configure the cache for a specific route, create (or edit) the config.ts file in that route's folder:
import { JopiRouteConfig } from "jopijs";
export default function(config: JopiRouteConfig) {
// Configure your cache hooks here
}Disabling the Cache
Method 1: The File (Static)
The simplest way to disable caching for a route is to create an empty file named autoCache.disable inside the route folder (next to your page.tsx).
src/
└── mod_example/
└── @routes/
└── my-page/
├── page.tsx
└── autoCache.disable <-- Just create this fileMethod 2: The Code (Dynamic)
You can also programmatically disable the HTML Cache for the page via config.ts. This is useful if you want to disable it based on certain conditions (though for totally dynamic logic, prefer htmlCache_beforeCheckingCache hooks).
config.onPage.htmlCache_disableAutomaticCache();Invalidating the Cache
You can manually invalidate cache entries using the req.htmlCache_removeFromCache() function available in the JopiRequest object. This is typically done when content changes (e.g., after an admin update).
1. In Hooks (config.ts)
You can use hooks to conditionally flush the cache for the current page.
// Inside config.ts
config.onPage.htmlCache_beforeCheckingCache(async (req) => {
// Example: If a specific query param is present, force a refresh
if (req.req_urlSearchParams.forceRefresh === "true") {
// Remove the current page from cache
await req.htmlCache_removeFromCache();
// The rest of the request will now generate a fresh page and cache it
}
});2. In API Routes
A common pattern is to clear the cache of a public page when an API endpoint modifies the underlying data.
// Inside an API route (e.g., POST /api/update-product)
config.onPOST.add_middleware(async (req) => {
// 1. Perform your update logic...
await updateProductInDb(req);
// 2. Build the URL of the page you want to clear
const publicPageUrl = new URL("https://site.com/products/my-product");
// 3. Remove that specific page from the cache
await req.htmlCache_removeFromCache(publicPageUrl);
});Cache Hooks
JopiJS exposes several hooks in the cache lifecycle allowing you to intercept, modify, or bypass the cache logic.
1. beforeCheckingCache
The htmlCache_beforeCheckingCache hook is the most powerful. It runs before the cache engine even looks for a cached file.
This is the perfect place to bypass the cache for specific users, such as administrators who always need to see the live version of the site to moderate content.
config.onPage.htmlCache_beforeCheckingCache(async (req) => {
// Determine if the user has the 'admin' role
if (req.role_userHasRole("admin")) {
// Force the request to bypass the cache and hit the server logic directly
req.htmlCache_removeFromCache();
}
});2. readCacheEntry
The htmlCache_readCacheEntry hook allows you to replace the default cache reading behavior.
Instead of JopiJS looking up the file in its internal storage (file system or memory), your function will be called. If you return a Response, JopiJS treats it as a cache hit. If you return undefined, it treats it as a cache miss and proceeds to generate the page.
config.onPage.htmlCache_readCacheEntry(async (req) => {
// Custom logic to retrieve the page from an external source (e.g., Redis, CDN)
const customCache = await myCustomCache.get(req.url);
if (customCache) {
return new Response(customCache.body, { headers: customCache.headers });
}
return undefined; // Cache miss
});3. beforeAddToCache
The htmlCache_beforeAddToCache hook allows you to inspect or modify the generated response before it is saved to the cache storage.
Important Header Rules: By default, the cache engine only preserves the following headers:
Content-TypeETag(automatically recalculated)Last-Modified
Any other custom header you add to the response will be discarded.
4. afterGetFromCache
The htmlCache_afterGetFromCache hook runs after a response has been retrieved from the cache but before it is sent to the client.
Use this for:
- Adding dynamic headers that shouldn't be frozen in time (e.g., current server time, request ID).
- Logging cache hits.
config.onPage.htmlCache_afterGetFromCache(async (req, res) => {
// Log a cache hit
console.log(`Cache HIT for ${req.url}`);
// Add a dynamic header
res.headers.set("X-Cache-Status", "HIT");
return res;
});5. ifNotInCache
The htmlCache_ifNotInCache hook is triggered when a request is made but the page is not found in the cache (a "cache miss").
This is primarily useful for:
- Logging cache misses.
- Triggering background jobs or pre-fetching related data.
config.onPage.htmlCache_ifNotInCache((req) => {
console.log(`Cache MISS for ${req.url} - Generating page...`);
});Global Cache Rules
For broader control over caching policies, you can define Global Cache Rules at the application level. This is perfect for applying rules to entire sections of your website (e.g., all /api routes) without having to configure each route individually.
Use the add_cacheRules method within the configure_htmlCache() block of your server initialization (typically src/index.ts).
Usage Example
import { JopiWebSiteBuilder } from "jopijs";
export default async function(app: JopiWebSiteBuilder) {
app.configure_htmlCache()
.add_cacheRules({
// 1. Define the scope of the rule using RouteSelector
routeSelector: {
fromPath: "/api",
exclude: ["/api/health", "/api/metrics"], // Don't apply this rule to these paths
},
// 2. Define the behavior
// Example: Disable automatic caching for all API routes except health/metrics
disableAutomaticCache: true,
// You can also use the standard hooks here:
// beforeCheckingCache: ...,
// afterGetFromCache: ...,
// beforeAddToCache: ...,
// ifNotInCache: ...,
});
}