Gathering detailed insights and metrics for @web-widget/shared-cache
Gathering detailed insights and metrics for @web-widget/shared-cache
Gathering detailed insights and metrics for @web-widget/shared-cache
Gathering detailed insights and metrics for @web-widget/shared-cache
🚀 Standards-compliant HTTP cache implementation for server-side JavaScript with RFC 7234 compliance and cross-runtime support
npm install @web-widget/shared-cache
Typescript
Module System
Node Version
NPM Version
TypeScript (99.82%)
JavaScript (0.18%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
7 Stars
76 Commits
1 Watchers
1 Branches
1 Contributors
Updated on Jun 30, 2025
Latest Version
1.7.0
Package Id
@web-widget/shared-cache@1.7.0
Unpacked Size
115.82 kB
Size
30.55 kB
File Count
5
NPM Version
10.9.2
Node Version
22.16.0
Published on
Jun 30, 2025
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
22
A standards-compliant HTTP cache implementation for server-side applications.
SharedCache is an HTTP caching library that follows Web Standards and HTTP specifications. It implements a cache interface similar to the Web Cache API but optimized for server-side shared caching scenarios.
stale-if-error
and stale-while-revalidate
Vary
headers, proxy revalidation, and authenticated responsesfetch
API with caching capabilities while maintaining full compatibilitys-maxage
over max-age
for shared cache performanceWhile the Web fetch
API has become ubiquitous in server-side JavaScript, existing browser Cache APIs are designed for single-user scenarios. Server-side applications need shared caches that serve multiple users efficiently.
SharedCache provides:
caches
API not availablecaches
APIlru-cache
directly1// Cache API responses to reduce backend load 2const apiFetch = createFetch(cache, { 3 defaults: { cacheControlOverride: 's-maxage=300' }, 4}); 5const userData = await apiFetch('/api/user/profile'); // First: 200ms, subsequent: 2ms
1// Cache rendered pages using HTTP cache control directives 2export const handler = { 3 async GET(ctx) { 4 const response = await ctx.render(); 5 6 // Set cache control headers for shared cache optimization 7 response.headers.set( 8 'cache-control', 9 's-maxage=60, ' + // Cache for 60 seconds in shared caches 10 'stale-if-error=604800, ' + // Serve stale content for 7 days on errors 11 'stale-while-revalidate=604800' // Background revalidation for 7 days 12 ); 13 14 return response; 15 }, 16};
Integration Requirements: This pattern requires web framework integration with SharedCache middleware or custom cache implementation in your SSR pipeline.
1// Same code works in Node.js, Deno, Bun, and Edge Runtime 2const fetch = createFetch(cache); 3// Deploy anywhere without code changes
1// Redis backend for multi-instance applications 2const caches = new CacheStorage(createRedisStorage()); 3const cache = await caches.open('distributed-cache');
1npm install @web-widget/shared-cache
1# Using yarn 2yarn add @web-widget/shared-cache 3 4# Using pnpm 5pnpm add @web-widget/shared-cache
Here's a simple example to get you started with SharedCache:
1import { 2 CacheStorage, 3 createFetch, 4 type KVStorage, 5} from '@web-widget/shared-cache'; 6import { LRUCache } from 'lru-cache'; 7 8// Create a storage backend using LRU cache 9const createLRUCache = (): KVStorage => { 10 const store = new LRUCache<string, any>({ max: 1024 }); 11 12 return { 13 async get(cacheKey: string) { 14 return store.get(cacheKey); 15 }, 16 async set(cacheKey: string, value: any, ttl?: number) { 17 store.set(cacheKey, value, { ttl }); 18 }, 19 async delete(cacheKey: string) { 20 return store.delete(cacheKey); 21 }, 22 }; 23}; 24 25// Initialize cache storage 26const caches = new CacheStorage(createLRUCache()); 27 28async function example() { 29 const cache = await caches.open('api-cache-v1'); 30 31 // Create fetch with default configuration 32 const fetch = createFetch(cache, { 33 defaults: { 34 cacheControlOverride: 's-maxage=300', // 5 minutes default caching 35 ignoreRequestCacheControl: true, 36 }, 37 }); 38 39 // First request - will hit the network 40 console.time('First request'); 41 const response1 = await fetch( 42 'https://httpbin.org/response-headers?cache-control=max-age%3D604800' 43 ); 44 console.timeEnd('First request'); // ~400ms 45 46 // Second request - served from cache 47 console.time('Cached request'); 48 const response2 = await fetch( 49 'https://httpbin.org/response-headers?cache-control=max-age%3D604800' 50 ); 51 console.timeEnd('Cached request'); // ~2ms 52 53 // Check cache status 54 console.log('Cache status:', response2.headers.get('x-cache-status')); // "HIT" 55} 56 57example();
This package exports a comprehensive set of APIs for HTTP caching functionality:
1import { 2 createFetch, // Main fetch function with caching 3 Cache, // SharedCache class 4 CacheStorage, // SharedCacheStorage class 5} from '@web-widget/shared-cache'; 6 7const cache = await caches.open('api-cache-v1'); 8const fetch = createFetch(cache, { 9 defaults: { 10 cacheControlOverride: 's-maxage=300', 11 ignoreRequestCacheControl: true, 12 }, 13});
1import { createFetch } from '@web-widget/shared-cache'; 2 3const cache = await caches.open('api-cache-v1'); 4const fetch = createFetch(cache, { 5 defaults: { 6 cacheControlOverride: 's-maxage=300', // 5 minutes default 7 }, 8}); 9 10// Simple usage - automatic caching 11const userData = await fetch('/api/user/profile'); 12const sameData = await fetch('/api/user/profile'); // Served from cache
1import Redis from 'ioredis'; 2import { 3 CacheStorage, 4 createFetch, 5 type KVStorage, 6} from '@web-widget/shared-cache'; 7 8const createRedisStorage = (): KVStorage => { 9 const redis = new Redis(process.env.REDIS_URL); 10 11 return { 12 async get(key: string) { 13 const value = await redis.get(key); 14 return value ? JSON.parse(value) : undefined; 15 }, 16 async set(key: string, value: any, ttl?: number) { 17 const serialized = JSON.stringify(value); 18 if (ttl) { 19 await redis.setex(key, Math.ceil(ttl / 1000), serialized); 20 } else { 21 await redis.set(key, serialized); 22 } 23 }, 24 async delete(key: string) { 25 return (await redis.del(key)) > 0; 26 }, 27 }; 28}; 29 30const caches = new CacheStorage(createRedisStorage()); 31const cache = await caches.open('distributed-cache'); 32const fetch = createFetch(cache, { 33 defaults: { 34 cacheControlOverride: 's-maxage=600', 35 cacheKeyRules: { 36 header: { include: ['x-tenant-id'] }, // Multi-tenant support 37 }, 38 }, 39});
1const deviceAwareFetch = createFetch(await caches.open('content-cache'), { 2 defaults: { 3 cacheControlOverride: 's-maxage=600', 4 cacheKeyRules: { 5 device: true, // Separate cache for mobile/desktop/tablet 6 search: { exclude: ['timestamp'] }, 7 }, 8 }, 9}); 10 11const response = await deviceAwareFetch('/api/content');
1const advancedFetch = createFetch(await caches.open('advanced-cache'), { 2 defaults: { 3 cacheControlOverride: 's-maxage=300, stale-while-revalidate=3600', 4 cacheKeyRules: { 5 search: { exclude: ['timestamp', '_'] }, 6 header: { include: ['x-api-version'] }, 7 cookie: { include: ['session_id'] }, 8 device: true, 9 }, 10 }, 11});
1import crypto from 'crypto'; 2 3const createEncryptedStorage = ( 4 baseStorage: KVStorage, 5 key: string 6): KVStorage => { 7 const encrypt = (text: string) => { 8 const cipher = crypto.createCipher('aes192', key); 9 let encrypted = cipher.update(text, 'utf8', 'hex'); 10 encrypted += cipher.final('hex'); 11 return encrypted; 12 }; 13 14 const decrypt = (text: string) => { 15 const decipher = crypto.createDecipher('aes192', key); 16 let decrypted = decipher.update(text, 'hex', 'utf8'); 17 decrypted += decipher.final('utf8'); 18 return decrypted; 19 }; 20 21 return { 22 async get(cacheKey: string) { 23 const encrypted = await baseStorage.get(cacheKey); 24 return encrypted ? JSON.parse(decrypt(encrypted as string)) : undefined; 25 }, 26 async set(cacheKey: string, value: unknown, ttl?: number) { 27 const encrypted = encrypt(JSON.stringify(value)); 28 return baseStorage.set(cacheKey, encrypted, ttl); 29 }, 30 async delete(cacheKey: string) { 31 return baseStorage.delete(cacheKey); 32 }, 33 }; 34}; 35 36const secureStorage = createEncryptedStorage(baseStorage, 'my-secret-key'); 37const caches = new CacheStorage(secureStorage);
1const tenantFetch = createFetch(await caches.open('tenant-cache'), { 2 defaults: { 3 cacheControlOverride: 's-maxage=300', 4 cacheKeyRules: { 5 header: { include: ['x-tenant-id'] }, 6 search: true, 7 }, 8 }, 9}); 10 11// Each tenant gets isolated cache 12const response = await tenantFetch('/api/data', { 13 headers: { 'x-tenant-id': 'tenant-123' }, 14});
1// Production-ready example with automatic token refresh 2const createAuthenticatedFetch = (getToken) => { 3 return async (input, init) => { 4 const token = await getToken(); 5 const headers = new Headers(init?.headers); 6 headers.set('Authorization', `Bearer ${token}`); 7 8 const response = await globalThis.fetch(input, { 9 ...init, 10 headers, 11 }); 12 13 // Handle token expiration 14 if (response.status === 401) { 15 // Token might be expired, retry once with fresh token 16 const freshToken = await getToken(true); // force refresh 17 headers.set('Authorization', `Bearer ${freshToken}`); 18 19 return globalThis.fetch(input, { 20 ...init, 21 headers, 22 }); 23 } 24 25 return response; 26 }; 27}; 28 29const authFetch = createFetch(await caches.open('authenticated-api'), { 30 fetch: createAuthenticatedFetch(() => getApiToken()), 31 defaults: { 32 cacheControlOverride: 33 'public, ' + // Required: Allow caching of authenticated requests 34 's-maxage=300', 35 cacheKeyRules: { 36 header: { include: ['authorization'] }, // Cache per token 37 }, 38 }, 39}); 40 41const userData = await authFetch('/api/user/profile');
For applications that need a global cache instance, you can set up the caches
object:
1import { CacheStorage, type KVStorage } from '@web-widget/shared-cache'; 2import { LRUCache } from 'lru-cache'; 3 4// Extend global types for TypeScript support 5declare global { 6 interface WindowOrWorkerGlobalScope { 7 caches: CacheStorage; 8 } 9} 10 11const createLRUCache = (): KVStorage => { 12 const store = new LRUCache<string, any>({ 13 max: 1024, 14 ttl: 1000 * 60 * 60, // 1 hour default TTL 15 }); 16 17 return { 18 async get(cacheKey: string) { 19 return store.get(cacheKey); 20 }, 21 async set(cacheKey: string, value: any, ttl?: number) { 22 store.set(cacheKey, value, { ttl }); 23 }, 24 async delete(cacheKey: string) { 25 return store.delete(cacheKey); 26 }, 27 }; 28}; 29 30// Set up global cache storage 31const caches = new CacheStorage(createLRUCache()); 32globalThis.caches = caches;
Once the global caches
is configured, you can also register a globally cached fetch
:
1import { createFetch } from '@web-widget/shared-cache'; 2 3// Replace global fetch with cached version 4globalThis.fetch = createFetch(await caches.open('default'), { 5 defaults: { 6 cacheControlOverride: 's-maxage=60', // 1 minute default for global fetch 7 }, 8});
The createFetch
API allows you to set default cache configuration:
1import { createFetch } from '@web-widget/shared-cache'; 2 3const cache = await caches.open('api-cache'); 4 5// Create fetch with comprehensive defaults 6const fetch = createFetch(cache, { 7 defaults: { 8 cacheControlOverride: 's-maxage=300', 9 cacheKeyRules: { 10 header: { include: ['x-api-version'] }, 11 }, 12 ignoreRequestCacheControl: true, 13 ignoreVary: false, 14 }, 15}); 16 17// Use with defaults applied automatically 18const response1 = await fetch('/api/data'); 19 20// Override defaults for specific requests 21const response2 = await fetch('/api/data', { 22 sharedCache: { 23 cacheControlOverride: 's-maxage=600', // Override default 24 }, 25});
The createFetch
function accepts a custom fetch implementation, allowing you to integrate with existing HTTP clients or add cross-cutting concerns:
1// Example: Integration with axios 2import axios from 'axios'; 3 4const axiosFetch = async (input, init) => { 5 const response = await axios({ 6 url: input.toString(), 7 method: init?.method || 'GET', 8 headers: init?.headers, 9 data: init?.body, 10 validateStatus: () => true, // Don't throw on 4xx/5xx 11 }); 12 13 return new Response(response.data, { 14 status: response.status, 15 statusText: response.statusText, 16 headers: response.headers, 17 }); 18}; 19 20const fetch = createFetch(await caches.open('axios-cache'), { 21 fetch: axiosFetch, 22 defaults: { 23 cacheControlOverride: 's-maxage=300', 24 }, 25}); 26 27// Example: Custom fetch with request/response transformation 28const transformFetch = async (input, init) => { 29 // Transform request 30 const url = new URL(input); 31 url.searchParams.set('timestamp', Date.now().toString()); 32 33 const response = await globalThis.fetch(url, init); 34 35 // Transform response 36 if (response.headers.get('content-type')?.includes('application/json')) { 37 const data = await response.json(); 38 const transformedData = { 39 ...data, 40 fetchedAt: new Date().toISOString(), 41 }; 42 43 return new Response(JSON.stringify(transformedData), { 44 status: response.status, 45 statusText: response.statusText, 46 headers: response.headers, 47 }); 48 } 49 50 return response; 51}; 52 53const transformedFetch = createFetch(await caches.open('transform-cache'), { 54 fetch: transformFetch, 55 defaults: { 56 cacheControlOverride: 's-maxage=300', 57 }, 58});
SharedCache extends the standard fetch API with caching options via the sharedCache
parameter:
1const cache = await caches.open('api-cache'); 2const fetch = createFetch(cache); 3 4const response = await fetch('https://api.example.com/data', { 5 // Standard fetch options 6 method: 'GET', 7 headers: { 8 'x-user-id': '1024', 9 }, 10 11 // SharedCache-specific options 12 sharedCache: { 13 cacheControlOverride: 's-maxage=120', 14 varyOverride: 'accept-language', 15 ignoreRequestCacheControl: true, 16 ignoreVary: false, 17 cacheKeyRules: { 18 search: false, 19 device: true, 20 header: { 21 include: ['x-user-id'], 22 }, 23 }, 24 }, 25});
cacheControlOverride
Override or extend cache control directives when APIs don't provide optimal caching headers:
1// Add shared cache directive 2sharedCache: { 3 cacheControlOverride: 's-maxage=3600'; 4} 5 6// Combine multiple directives 7sharedCache: { 8 cacheControlOverride: 's-maxage=3600, must-revalidate'; 9}
varyOverride
Add additional Vary headers to ensure proper cache segmentation:
1sharedCache: { 2 varyOverride: 'accept-language, user-agent'; 3}
ignoreRequestCacheControl
Control whether to honor cache-control directives from the request:
1// Ignore client cache-control headers (default: true) 2sharedCache: { 3 ignoreRequestCacheControl: false; 4}
ignoreVary
Disable Vary header processing for simplified caching:
1sharedCache: { 2 ignoreVary: true; // Cache regardless of Vary headers 3}
cacheKeyRules
Customize how cache keys are generated to optimize cache hit rates and handle different caching scenarios:
1sharedCache: { 2 cacheKeyRules: { 3 // URL components 4 search: true, // Include query parameters (default) 5 6 // Request context 7 device: false, // Classify by device type 8 cookie: { // Include specific cookies 9 include: ['session_id', 'user_pref'] 10 }, 11 header: { // Include specific headers 12 include: ['x-api-key'], 13 checkPresence: ['x-mobile-app'] 14 } 15 } 16}
Default cache key rules:
1{ 2 search: true, 3}
search
: Control query parameter inclusionQuery Parameter Control:
1// Include all query parameters (default) 2search: true; 3 4// Exclude all query parameters 5search: false; 6 7// Include specific parameters 8search: { 9 include: ['category', 'page']; 10} 11 12// Include all except specific parameters 13search: { 14 exclude: ['timestamp', 'nonce']; 15}
Automatically classify requests as mobile
, desktop
, or tablet
based on User-Agent:
1cacheKeyRules: { 2 device: true; // Separate cache for different device types 3}
Include specific cookies in the cache key:
1cacheKeyRules: { 2 cookie: { 3 include: ['user_id', 'session_token'], 4 checkPresence: ['is_premium'] // Check existence without value 5 } 6}
Include request headers in the cache key:
1cacheKeyRules: { 2 header: { 3 include: ['x-api-version'], 4 checkPresence: ['x-feature-flag'] 5 } 6}
Restricted Headers: For security and performance, certain headers cannot be included:
accept
, accept-charset
, accept-encoding
, accept-language
, user-agent
, referer
cache-control
, if-*
, range
, connection
authorization
, cookie
(handled separately by cookie rules)host
SharedCache provides comprehensive monitoring through the x-cache-status
header for debugging and performance analysis.
Status | Description | When It Occurs |
---|---|---|
HIT | Response served from cache | The requested resource was found in cache and is still fresh |
MISS | Response fetched from origin | The requested resource was not found in cache |
EXPIRED | Cached response expired, fresh response fetched | The cached response exceeded its TTL |
STALE | Stale response served | Served due to stale-while-revalidate or stale-if-error |
BYPASS | Cache bypassed | Bypassed due to cache control directives like no-store |
REVALIDATED | Cached response revalidated | Response validated with origin (304 Not Modified) |
DYNAMIC | Response cannot be cached | Cannot be cached due to HTTP method or status code |
The x-cache-status
header is automatically added to all responses:
HIT
, MISS
, EXPIRED
, STALE
, BYPASS
, REVALIDATED
, DYNAMIC
SharedCache provides a comprehensive logging system with structured output for monitoring and debugging cache operations.
1interface Logger { 2 info(message?: unknown, ...optionalParams: unknown[]): void; 3 warn(message?: unknown, ...optionalParams: unknown[]): void; 4 debug(message?: unknown, ...optionalParams: unknown[]): void; 5 error(message?: unknown, ...optionalParams: unknown[]): void; 6}
1import { createLogger, LogLevel } from '@web-widget/shared-cache'; 2 3// Create a simple console logger 4const logger = { 5 info: console.info.bind(console), 6 warn: console.warn.bind(console), 7 debug: console.debug.bind(console), 8 error: console.error.bind(console), 9}; 10 11// Create SharedCache with logger 12const cache = new SharedCache(storage, { 13 logger, 14});
SharedCache: Cache miss { url: 'https://api.com/data', cacheKey: 'api:data', method: 'GET' }
SharedCache: Cache item found { url: 'https://api.com/data', cacheKey: 'api:data', method: 'GET' }
SharedCache: Cache hit { url: 'https://api.com/data', cacheKey: 'api:data', cacheStatus: 'HIT' }
SharedCache: Serving stale response - Revalidating in background { url: 'https://api.com/data', cacheKey: 'api:data', cacheStatus: 'STALE' }
SharedCache: Revalidation network error - Using fallback 500 response { url: 'https://api.com/data', cacheKey: 'api:data', error: [NetworkError] }
SharedCache: Put operation failed { url: 'https://api.com/data', error: [StorageError] }
SharedCache: Revalidation failed - Server returned 5xx status { url: 'https://api.com/data', status: 503, cacheKey: 'api:data' }
1const productionLogger = { 2 info: (msg, ctx) => 3 console.log(JSON.stringify({ level: 'INFO', message: msg, ...ctx })), 4 warn: (msg, ctx) => 5 console.warn(JSON.stringify({ level: 'WARN', message: msg, ...ctx })), 6 debug: () => {}, // No debug in production 7 error: (msg, ctx) => 8 console.error(JSON.stringify({ level: 'ERROR', message: msg, ...ctx })), 9}; 10 11const cache = new SharedCache(storage, { 12 logger: productionLogger, 13});
1const devLogger = { 2 info: console.info.bind(console), 3 warn: console.warn.bind(console), 4 debug: console.debug.bind(console), 5 error: console.error.bind(console), 6}; 7 8const cache = new SharedCache(storage, { 9 logger: devLogger, 10});
1import { createLogger, LogLevel } from '@web-widget/shared-cache'; 2 3class CustomLogger { 4 info(message: unknown, ...params: unknown[]) { 5 this.log('INFO', message, ...params); 6 } 7 8 warn(message: unknown, ...params: unknown[]) { 9 this.log('WARN', message, ...params); 10 } 11 12 debug(message: unknown, ...params: unknown[]) { 13 this.log('DEBUG', message, ...params); 14 } 15 16 error(message: unknown, ...params: unknown[]) { 17 this.log('ERROR', message, ...params); 18 } 19 20 private log(level: string, message: unknown, ...params: unknown[]) { 21 const timestamp = new Date().toISOString(); 22 const context = params[0] || {}; 23 console.log( 24 JSON.stringify({ 25 timestamp, 26 level, 27 service: 'shared-cache', 28 message, 29 ...context, 30 }) 31 ); 32 } 33} 34 35const customLogger = new CustomLogger(); 36const structuredLogger = createLogger(customLogger, LogLevel.DEBUG); 37 38const cache = new SharedCache(storage, { 39 logger: customLogger, 40});
All log messages include structured context data:
1interface SharedCacheLogContext { 2 url?: string; // Request URL 3 cacheKey?: string; // Generated cache key 4 status?: number; // HTTP status code 5 duration?: number; // Operation duration (ms) 6 error?: unknown; // Error object 7 cacheStatus?: string; // Cache result status 8 ttl?: number; // Time to live (seconds) 9 method?: string; // HTTP method 10 [key: string]: unknown; // Additional context 11}
1import { createLogger, LogLevel } from '@web-widget/shared-cache'; 2 3let hitCount = 0; 4let totalCount = 0; 5 6const monitoringLogger = { 7 info: (message, context) => { 8 if (context?.cacheStatus) { 9 totalCount++; 10 if (context.cacheStatus === 'HIT') hitCount++; 11 12 // Log hit rate every 100 requests 13 if (totalCount % 100 === 0) { 14 console.log( 15 `Cache hit rate: ${((hitCount / totalCount) * 100).toFixed(2)}%` 16 ); 17 } 18 } 19 console.log(message, context); 20 }, 21 warn: console.warn, 22 debug: console.debug, 23 error: console.error, 24}; 25 26const cache = new SharedCache(storage, { 27 logger: createLogger(monitoringLogger, LogLevel.INFO), 28});
1const performanceLogger = { 2 info: (message, context) => { 3 if (context?.duration) { 4 console.log(`${message} - Duration: ${context.duration}ms`, context); 5 } else { 6 console.log(message, context); 7 } 8 }, 9 warn: console.warn, 10 debug: console.debug, 11 error: console.error, 12};
1const alertingLogger = { 2 info: console.log, 3 warn: console.warn, 4 debug: console.debug, 5 error: (message, context) => { 6 console.error(message, context); 7 8 // Send alerts for critical cache errors 9 if (context?.error && message.includes('Put operation failed')) { 10 sendAlert(`Cache storage error: ${context.error.message}`); 11 } 12 }, 13};
Main Functions:
createFetch(cache?, options?)
- Create cached fetch functioncreateLogger(logger?, logLevel?, prefix?)
- Create logger with level filteringClasses:
Cache
- Main cache implementationCacheStorage
- Cache storage managerKey Types:
KVStorage
- Storage backend interfaceSharedCacheRequestInitProperties
- Request cache configurationSharedCacheKeyRules
- Cache key generation rulesCreates a fetch function with shared cache configuration.
1function createFetch( 2 cache?: Cache, 3 options?: { 4 fetch?: typeof fetch; 5 defaults?: Partial<SharedCacheRequestInitProperties>; 6 } 7): SharedCacheFetch;
Parameters:
cache
- Optional SharedCache instance (auto-discovered from globalThis.caches if not provided)options.fetch
- Custom fetch implementation to use as the underlying fetcher (defaults to globalThis.fetch)options.defaults
- Default shared cache options to apply to all requestsReturns: SharedCacheFetch
- A fetch function with caching capabilities
Basic Usage:
1const cache = await caches.open('my-cache'); 2const fetch = createFetch(cache, { 3 defaults: { cacheControlOverride: 's-maxage=300' }, 4});
Request-level cache configuration:
1interface SharedCacheRequestInitProperties { 2 cacheControlOverride?: string; 3 cacheKeyRules?: SharedCacheKeyRules; 4 ignoreRequestCacheControl?: boolean; 5 ignoreVary?: boolean; 6 varyOverride?: string; 7 event?: ExtendableEvent; 8}
Cache key generation rules:
1interface SharedCacheKeyRules { 2 cookie?: FilterOptions | boolean; 3 device?: FilterOptions | boolean; 4 header?: FilterOptions | boolean; 5 search?: FilterOptions | boolean; 6}
Storage backend interface:
1interface KVStorage { 2 get: (cacheKey: string) => Promise<unknown | undefined>; 3 set: (cacheKey: string, value: unknown, ttl?: number) => Promise<void>; 4 delete: (cacheKey: string) => Promise<boolean>; 5}
1class Cache { 2 match(request: RequestInfo | URL): Promise<Response | undefined>; 3 put(request: RequestInfo | URL, response: Response): Promise<void>; 4 delete(request: RequestInfo | URL): Promise<boolean>; 5} 6 7class CacheStorage { 8 constructor(storage: KVStorage); 9 open(cacheName: string): Promise<Cache>; 10}
createLogger(logger?, logLevel?, prefix?)
1const logger = createLogger(console, LogLevel.INFO, 'MyApp');
Creates a structured logger with level filtering and optional prefix.
1type SharedCacheStatus = 2 | 'HIT' 3 | 'MISS' 4 | 'EXPIRED' 5 | 'STALE' 6 | 'BYPASS' 7 | 'REVALIDATED' 8 | 'DYNAMIC';
Status values are automatically added to response headers as x-cache-status
.
Complete API documentation available in TypeScript definitions and source code.
SharedCache demonstrates exceptional HTTP standards compliance, fully adhering to established web caching specifications:
Complete Compliance Features:
no-store
, no-cache
, private
, public
, s-maxage
, and max-age
SharedCache implements a subset of the standard Web Cache API interface, focusing on core caching operations:
1interface Cache { 2 match(request: RequestInfo | URL): Promise<Response | undefined>; // ✅ Implemented 3 put(request: RequestInfo | URL, response: Response): Promise<void>; // ✅ Implemented 4 delete(request: RequestInfo | URL): Promise<boolean>; // ✅ Implemented 5 6 // Not implemented - throw "not implemented" errors 7 add(request: RequestInfo | URL): Promise<void>; // ❌ Throws error 8 addAll(requests: RequestInfo[]): Promise<void>; // ❌ Throws error 9 keys(): Promise<readonly Request[]>; // ❌ Throws error 10 matchAll(): Promise<readonly Response[]>; // ❌ Throws error 11}
Implementation Status:
match()
, put()
, delete()
- Fully implemented with HTTP semanticsadd()
, addAll()
- Use put()
insteadkeys()
, matchAll()
- Not available in server environmentsOptions Parameter Differences:
SharedCache's CacheQueryOptions
interface differs from the standard Web Cache API:
1interface CacheQueryOptions { 2 ignoreSearch?: boolean; // ❌ Not implemented - throws error 3 ignoreMethod?: boolean; // ✅ Supported 4 ignoreVary?: boolean; // ❌ Not implemented - throws error 5}
Supported Options:
ignoreMethod
: Treat request as GET regardless of actual HTTP methodUnsupported Options (throw errors):
ignoreSearch
: Query string handling not customizableignoreVary
: Vary header processing not bypassableStandard | Status | Coverage |
---|---|---|
RFC 7234 (HTTP Caching) | ✅ Fully Compliant | 100% |
RFC 5861 (stale-* extensions) | ✅ Fully Compliant | 100% |
Web Cache API | ✅ Subset Implementation | Core Methods |
WinterCG Standards | ✅ Fully Supported | 100% |
http-cache-semantics
for RFC complianceprivate
directive for user-specific contents-maxage
over max-age
for multi-user environmentsAuthorization
headers are not cached in shared caches unless explicitly permitted by response cache control directives🔒 Important Security Note: SharedCache automatically enforces HTTP caching security rules. Requests containing Authorization
headers will not be cached unless the response explicitly allows it with directives like public
, s-maxage
, or must-revalidate
. This ensures compliance with shared cache security requirements.
SharedCache is production-ready and battle-tested, providing enterprise-grade HTTP caching with full standards compliance for server-side applications.
A: Absolutely! SharedCache supports any storage backend that implements the KVStorage
interface:
1// Redis example 2const redisStorage: KVStorage = { 3 async get(key) { 4 return JSON.parse((await redis.get(key)) || 'null'); 5 }, 6 async set(key, value, ttl) { 7 await redis.setex(key, ttl / 1000, JSON.stringify(value)); 8 }, 9 async delete(key) { 10 return (await redis.del(key)) > 0; 11 }, 12};
A: SharedCache handles concurrent requests efficiently by serving cache entries and avoiding duplicate network requests.
A: SharedCache is technically compatible with edge runtimes, but it's typically not needed in edge environments. Most edge runtimes (Cloudflare Workers, Vercel Edge Runtime, Deno Deploy) already provide native caches
API implementation.
Primary Use Cases for SharedCache:
caches
API is not natively availableMigration Benefits:
When using SharedCache with meta-frameworks, you can develop with a consistent caching API and deploy to any environment - whether it has native caches
support or not. This provides true runtime portability for your caching logic.
stale-while-revalidate
and stale-if-error
directives?A: These RFC 5861 extensions provide significant performance and reliability benefits:
1// Best practice: Use both directives together 2const fetch = createFetch(cache, { 3 defaults: { 4 cacheControlOverride: 5 's-maxage=300, stale-while-revalidate=86400, stale-if-error=86400', 6 }, 7});
SharedCache draws inspiration from industry-leading caching implementations:
MIT License - see LICENSE file for details.
No vulnerabilities found.
No security vulnerabilities found.