or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

authentication.mdcaching.mdextensions.mdindex.mdplugin-system.mdrequest-processing.mdresponse-management.mdrouting.mdserver-management.mdvalidation.md
tile.json

caching.mddocs/

Caching

Built-in caching system with support for multiple backends and policies for server methods and general purpose caching in @hapi/hapi.

Capabilities

Cache Policy Creation

Create cache policies for storing and retrieving data with configurable expiration and backend storage.

/**
 * Create a cache policy
 * @param options - Cache policy configuration
 * @returns Cache policy instance
 */
cache(options: CachePolicyOptions): CachePolicy;

interface CachePolicyOptions {
    /** Cache segment name */
    segment?: string;
    /** Cache partition name */
    partition?: string;
    /** Shared cache policy name */
    policy?: string;
    /** Cache expiration in milliseconds */
    expiresIn?: number;
    /** Cache expiration at specific time */
    expiresAt?: string;
    /** Stale cache usage time in milliseconds */
    staleIn?: number;
    /** Stale cache timeout in milliseconds */
    staleTimeout?: number;
    /** Generate cache key function */
    generateFunc?: Function;
    /** Generate cache timeout in milliseconds */
    generateTimeout?: number;
    /** Drop on generate error */
    dropOnError?: boolean;
    /** Pending generate timeout in milliseconds */
    pendingGenerateTimeout?: number;
    /** Cache provider */
    cache?: string;
}

interface CachePolicy {
    /** Get value from cache */
    get(key: string): Promise<CacheResult>;
    /** Set value in cache */
    set(key: string, value: any, ttl?: number): Promise<void>;
    /** Remove value from cache */
    drop(key: string): Promise<void>;
    /** Check if key exists in cache */
    isReady(): boolean;
    /** Cache statistics */
    stats: CacheStats;
}

Usage Examples:

const server = Hapi.server({ port: 3000 });

// Create basic cache policy
const cache = server.cache({
    segment: 'user-cache',
    expiresIn: 60 * 1000 // 1 minute
});

// Use cache in route handler
server.route({
    method: 'GET',
    path: '/users/{id}',
    handler: async (request, h) => {
        const userId = request.params.id;
        const cacheKey = `user-${userId}`;
        
        // Try to get from cache first
        const cached = await cache.get(cacheKey);
        if (cached) {
            return cached.item;
        }
        
        // Fetch from database
        const user = await database.getUser(userId);
        
        // Store in cache
        await cache.set(cacheKey, user);
        
        return user;
    }
});

// Cache with custom key generation
const sessionCache = server.cache({
    segment: 'sessions',
    expiresIn: 30 * 60 * 1000, // 30 minutes
    generateFunc: async (id) => {
        return await database.getSession(id);
    },
    generateTimeout: 5000
});

Server Method Caching

Cache server methods with automatic key generation and cache invalidation.

/**
 * Register server method with caching
 * @param name - Method name or method configuration
 * @param method - Method function (if name is string)
 * @param options - Method options including caching
 */
method(name: string | MethodObject, method?: Function, options?: MethodOptions): void;

interface MethodOptions {
    /** Context binding */
    bind?: object;
    /** Caching configuration */
    cache?: MethodCacheOptions;
    /** Generate key function */
    generateKey?: Function;
}

interface MethodCacheOptions {
    /** Cache expiration in milliseconds */
    expiresIn?: number;
    /** Cache expiration at specific time */
    expiresAt?: string;
    /** Stale cache usage time */
    staleIn?: number;
    /** Stale timeout */
    staleTimeout?: number;
    /** Generate function timeout */
    generateTimeout?: number;
    /** Generate on error flag */
    generateOnError?: boolean;
    /** Pending generate timeout */
    pendingGenerateTimeout?: number;
    /** Cache segment */
    segment?: string;
    /** Cache partition */
    partition?: string;
}

Usage Examples:

// Server method with caching
server.method('getUser', async (id) => {
    console.log('Fetching user from database:', id);
    return await database.getUser(id);
}, {
    cache: {
        expiresIn: 60 * 1000, // 1 minute
        generateTimeout: 5000
    },
    generateKey: (id) => `user:${id}`
});

// Use cached server method
server.route({
    method: 'GET',
    path: '/users/{id}',
    handler: async (request, h) => {
        // This will use cache automatically
        const user = await server.methods.getUser(request.params.id);
        return user;
    }
});

// Server method with complex caching
server.method('getPopularPosts', async (category, limit) => {
    return await database.getPopularPosts(category, limit);
}, {
    cache: {
        expiresIn: 10 * 60 * 1000, // 10 minutes
        staleIn: 5 * 60 * 1000,    // Allow stale for 5 minutes
        staleTimeout: 1000,         // 1 second stale timeout
        segment: 'popular-posts'
    },
    generateKey: (category, limit) => `popular:${category}:${limit}`
});

Cache Configuration

Configure cache providers and global cache settings.

interface ServerCacheOptions {
    /** Cache provider configuration */
    provider?: CacheProvider | string;
    /** Provider options */
    options?: object;
    /** Cache engine name */
    engine?: string;
    /** Cache partition */
    partition?: string;
    /** Shared cache instances */
    shared?: boolean;
}

interface CacheProvider {
    /** Provider constructor */
    constructor: Function;
    /** Provider options */
    options?: object;
}

Usage Examples:

// Server with Redis cache
const server = Hapi.server({
    port: 3000,
    cache: [
        {
            name: 'redis-cache',
            provider: {
                constructor: require('@hapi/catbox-redis'),
                options: {
                    host: 'localhost',
                    port: 6379,
                    database: 0
                }
            }
        },
        {
            name: 'memory-cache',
            provider: {
                constructor: require('@hapi/catbox-memory'),
                options: {
                    maxByteSize: 100 * 1024 * 1024 // 100MB
                }
            }
        }
    ]
});

// Use specific cache provider
const redisCache = server.cache({
    cache: 'redis-cache',
    segment: 'user-sessions',
    expiresIn: 24 * 60 * 60 * 1000 // 24 hours
});

Cache Result Handling

Handle cache results including stale data and cache misses.

interface CacheResult {
    /** Cached item */
    item: any;
    /** Item stored timestamp */
    stored: number;
    /** Item TTL in milliseconds */
    ttl: number;
    /** Whether item is stale */
    isStale: boolean;
}

Usage Examples:

server.route({
    method: 'GET',
    path: '/data/{id}',
    handler: async (request, h) => {
        const key = `data-${request.params.id}`;
        const result = await cache.get(key);
        
        if (result) {
            const response = h.response(result.item);
            
            // Add cache metadata headers
            response.header('X-Cache', 'HIT');
            response.header('X-Cache-Stored', new Date(result.stored).toISOString());
            response.header('X-Cache-TTL', Math.round(result.ttl / 1000));
            
            if (result.isStale) {
                response.header('X-Cache-Stale', 'true');
                // Refresh cache in background
                refreshDataInBackground(request.params.id, key);
            }
            
            return response;
        }
        
        // Cache miss - fetch and cache
        const data = await fetchData(request.params.id);
        await cache.set(key, data);
        
        return h.response(data).header('X-Cache', 'MISS');
    }
});

Cache Statistics and Monitoring

Monitor cache performance and statistics.

interface CacheStats {
    /** Cache hits */
    hits: number;
    /** Cache misses */
    misses: number;
    /** Cache sets */
    sets: number;
    /** Cache gets */
    gets: number;
    /** Cache errors */
    errors: number;
}

Usage Examples:

// Monitor cache performance
server.route({
    method: 'GET',
    path: '/cache/stats',
    handler: (request, h) => {
        const stats = cache.stats;
        const hitRate = stats.hits / (stats.hits + stats.misses) * 100;
        
        return {
            hits: stats.hits,
            misses: stats.misses,
            hitRate: `${hitRate.toFixed(2)}%`,
            sets: stats.sets,
            gets: stats.gets,
            errors: stats.errors
        };
    }
});

// Reset cache stats
server.route({
    method: 'POST',
    path: '/cache/reset-stats',
    handler: (request, h) => {
        cache.stats.reset();
        return { message: 'Cache stats reset' };
    }
});

Cache Warming and Management

Warm cache and manage cached data programmatically.

Usage Examples:

// Cache warming on server start
server.ext('onPostStart', async () => {
    console.log('Warming cache...');
    
    // Pre-populate frequently accessed data
    const popularUsers = await database.getPopularUsers();
    for (const user of popularUsers) {
        await cache.set(`user-${user.id}`, user);
    }
    
    console.log(`Warmed cache with ${popularUsers.length} users`);
});

// Cache invalidation
server.route({
    method: 'PUT',
    path: '/users/{id}',
    handler: async (request, h) => {
        const userId = request.params.id;
        
        // Update user
        const updatedUser = await database.updateUser(userId, request.payload);
        
        // Invalidate cache
        await cache.drop(`user-${userId}`);
        
        // Optionally warm cache with new data
        await cache.set(`user-${userId}`, updatedUser);
        
        return updatedUser;
    }
});

// Bulk cache operations
server.route({
    method: 'DELETE',
    path: '/cache/users',
    handler: async (request, h) => {
        const userIds = request.payload.userIds;
        
        // Drop multiple cache entries
        const promises = userIds.map(id => cache.drop(`user-${id}`));
        await Promise.all(promises);
        
        return { message: `Invalidated cache for ${userIds.length} users` };
    }
});

Advanced Caching Patterns

Complex caching scenarios and patterns.

Usage Examples:

// Cache with dependency invalidation
const createDependentCache = (server) => {
    const userCache = server.cache({
        segment: 'users',
        expiresIn: 60 * 60 * 1000 // 1 hour
    });
    
    const postCache = server.cache({
        segment: 'posts',
        expiresIn: 30 * 60 * 1000 // 30 minutes
    });
    
    return {
        async invalidateUser(userId) {
            await userCache.drop(`user-${userId}`);
            // Also invalidate user's posts
            const userPosts = await database.getUserPostIds(userId);
            for (const postId of userPosts) {
                await postCache.drop(`post-${postId}`);
            }
        }
    };
};

// Layered caching (L1 memory, L2 Redis)
const createLayeredCache = (server) => {
    const l1Cache = server.cache({
        cache: 'memory-cache',
        segment: 'l1',
        expiresIn: 5 * 60 * 1000 // 5 minutes
    });
    
    const l2Cache = server.cache({
        cache: 'redis-cache',
        segment: 'l2',
        expiresIn: 60 * 60 * 1000 // 1 hour
    });
    
    return {
        async get(key) {
            // Try L1 first
            let result = await l1Cache.get(key);
            if (result) {
                return result.item;
            }
            
            // Try L2
            result = await l2Cache.get(key);
            if (result) {
                // Populate L1
                await l1Cache.set(key, result.item);
                return result.item;
            }
            
            return null;
        },
        
        async set(key, value) {
            await Promise.all([
                l1Cache.set(key, value),
                l2Cache.set(key, value)
            ]);
        }
    };
};

Types

interface CacheProvisionOptions {
    /** Provider name */
    provider: string | CacheProvider;
    /** Provider options */
    options?: object;
}

interface MethodObject {
    /** Method name */
    name: string;
    /** Method function */
    method: Function;
    /** Method options */
    options?: MethodOptions;
}