Cache Management

StoneJS provides a powerful caching system to dramatically improve performance. Cache entire components, database queries, API responses, or computed values with simple, intuitive APIs.

Cache Statistics

Store Type: disk
Cache Size: 0 items
Default TTL: 7200 seconds
Page Renders: 1

Component Caching with cacheSelf()

Cache entire component output with a single call - perfect for expensive page generation, dhandlers, and high-traffic pages.

⏱️
Human-Readable TTL
Use natural time expressions: '1 minute', '3 hours', '30 sec', '7 days'
🔒
Busy Lock
Prevents cache stampede - only one request regenerates expired cache
🔑
URI-Based Keys
Defaults to request URI - perfect for dhandlers and dynamic routes
🚀
Easy Integration
Add one line to <%%init> section - that's it!

Quick Example

<%%init> // Cache this entire page for 3 hours with 10 second busy lock if (await $cache.cacheSelf({ expiresIn: '3 hours', busyLock: '10 sec' })) { return; // Cached version served, skip execution } // This code only runs on cache miss const expensiveData = await fetchFromDatabase(); </init%%>

Interactive Demo

Visit /demo/cache-self-demo.html to see cacheSelf() in action! That page caches itself for 1 minute - refresh it multiple times to see the timestamp stay frozen, then wait a minute and watch it update.

Try the live demo:

  1. Open the cacheSelf() demo page
  2. Note the "Page Generated At" timestamp
  3. Refresh multiple times - timestamp doesn't change (cached!)
  4. Wait 1 minute and refresh - timestamp updates (cache expired)

More Examples

Default Caching (URI-based)

<%%init> // Cache for 3 hours, automatic key based on URI if (await $cache.cacheSelf({ expiresIn: '3 hours' })) { return; } </init%%>

Custom Key for User-Specific Caching

<%%init> // Cache with custom key if (await $cache.cacheSelf({ key: `user-dashboard:${$session.userId}`, expiresIn: '30 minutes', busyLock: '10 sec' })) { return; } </init%%>

Dhandler with Per-URL Caching

// In /api/v1/dhandler <%%init> // Each URL cached separately! // /api/v1/users → key: '/api/v1/users' // /api/v1/posts → key: '/api/v1/posts' if (await $cache.cacheSelf({ expiresIn: '5 minutes', busyLock: '5 sec' })) { return; } // Generate API response based on $req.path... const endpoint = $req.path.split('/').pop(); </init%%>

API Reference

$cache.cacheSelf(options)

Option Type Default Description
key string $req.path Custom cache key (optional)
expiresIn string|number 3600 TTL in human format ('3 hours') or seconds
busyLock string|number '30 sec' Busy lock timeout to prevent cache stampede

Time Format Examples

Format Seconds
'30 sec'30
'1 minute'60
'5 minutes'300
'1 hour'3600
'3 hours'10800
'1 day'86400
'7 days'604800
'1 week'604800

Data Cache API Reference

Method Description
await $cache.set(key, value, ttl) Store a value in cache with TTL (in seconds or human format)
await $cache.get(key) Retrieve a value from cache (returns null if not found or expired)
await $cache.forget(key) Delete a specific cache key (alias: delete())
await $cache.remember(key, ttl, fn) Get from cache or execute callback and cache result (recommended pattern)
await $cache.flush() Clear all cached data
$cache.getStats() Get cache statistics (size, store type, default TTL)

Partial Caching (Data & Fragments)

For fine-grained control, cache specific data or HTML fragments within your components using the $cache API.

Cache Remember Pattern (Recommended)

Live Demo - Cached Timestamp (30 second TTL):

2025-12-26T10:47:48.278Z

Reload the page multiple times - this timestamp won't change for 30 seconds!

The remember pattern automatically handles cache miss and sets cache. The callback only runs if cache is empty:

<% const products = await $cache.remember('products-list', 600, async () => { // This function only runs if cache is empty return await $db.query('SELECT * FROM products'); }); %>

Interactive Cache Operations

1. Set Cache Value

2. Get Cache Value

3. Delete Cache Value

Basic Cache Operations

Set a value

<% await $cache.set('user-123', userData, 3600); %>

Get a value

<% const userData = await $cache.get('user-123'); %>

Delete a value

<% // Method 1: delete() await $cache.delete('user-123'); // Method 2: forget() - Laravel/Mason-style alias await $cache.forget('user-123'); %>

Clear all cache

<% await $cache.flush(); %>

Handle cache miss manually

<% let userData = await $cache.get('user-123'); if (!userData) { // Cache miss - fetch from database userData = await $db.query('SELECT * FROM users WHERE id = 123'); await $cache.set('user-123', userData, 3600); } %>

Fragment Caching

Cache expensive HTML generation for better performance:

<% let expensiveHtml = await $cache.get('sidebar-widget'); if (!expensiveHtml) { expensiveHtml = await renderComplexWidget(); await $cache.set('sidebar-widget', expensiveHtml, 300); } %> <%- expensiveHtml %>

Common Caching Patterns

1. Database Query Results

Cache database query results to reduce database load:

<% const users = await $cache.remember('all-users', 3600, async () => { return await $db.query('SELECT * FROM users'); }); %>

2. API Responses

Cache external API responses to improve response time and reduce API calls:

<% const weatherData = await $cache.remember('weather-nyc', 1800, async () => { const response = await fetch('https://api.weather.com/nyc'); return await response.json(); }); %>

3. Computed Values

Cache expensive computations:

<% const statistics = await $cache.remember('site-stats', 600, async () => { return { totalUsers: await countUsers(), totalPosts: await countPosts(), activeUsers: await countActiveUsers() }; }); %>

4. User-Specific Cache

Use dynamic cache keys for user-specific data:

<% const userId = $session.userId; const userPreferences = await $cache.remember( `user-prefs-${userId}`, 3600, async () => { return await $db.query( 'SELECT * FROM preferences WHERE user_id = $1', [userId] ); } ); %>

Advanced Topics

Cache Invalidation

When data changes, invalidate the cache:

<% // Update database await $db.query('UPDATE users SET name = $1 WHERE id = $2', [newName, userId]); // Invalidate affected cache entries await $cache.delete(`user-${userId}`); await $cache.delete('all-users'); %>

Cache Stampede Prevention

Both cacheSelf() and remember() automatically prevent cache stampedes. When cache expires under high load, only one request regenerates the data while others wait:

<% // Only one request executes this callback when cache expires const data = await $cache.remember('popular-data', 60, async () => { return await fetchExpensiveData(); }); %>

Cache Configuration

Configure cache behavior in your application:

const app = new StoneJS({ root: './pages', cache: { store: 'memory', defaultTTL: 3600 } });

Performance Benefits

Typical Performance Improvements:

Best Practices

Clear All Cache

This will clear all cached data from the cache store, including both component and data caches.