@@ -173,6 +173,180 @@ app.use(
173173- ` formatErrorMessage ` : Custom error log format function
174174- ` onLog ` : Callback function called after logging
175175
176+ ### Cache
177+
178+ High-performance HTTP caching middleware with support for multiple storage backends, conditional requests, and stale-while-revalidate.
179+
180+ ``` js
181+ import { cache , MemoryCache , LRUCache , RedisCache } from " @rabbit-company/web-middleware" ;
182+
183+ // Basic in-memory caching
184+ app .use (cache ());
185+
186+ // LRU cache with max 500 entries
187+ app .use (
188+ cache ({
189+ storage: new LRUCache (500 ),
190+ ttl: 600 , // 10 minutes
191+ })
192+ );
193+
194+ // Redis cache for distributed caching
195+ import Redis from " ioredis" ;
196+ const redis = new Redis ();
197+
198+ app .use (
199+ cache ({
200+ storage: new RedisCache (redis),
201+ ttl: 3600 , // 1 hour
202+ staleWhileRevalidate: true ,
203+ maxStaleAge: 86400 , // 24 hours
204+ })
205+ );
206+
207+ // Advanced configuration
208+ app .use (
209+ cache ({
210+ storage: new MemoryCache (),
211+ ttl: 300 , // 5 minutes default
212+ methods: [" GET" , " HEAD" ], // Only cache GET/HEAD requests
213+
214+ // Custom cache key generation
215+ keyGenerator : (ctx ) => {
216+ const url = new URL (ctx .req .url );
217+ const userId = ctx .get (" user" )? .id || " anonymous" ;
218+ return ` ${ ctx .req .method } :${ url .pathname } :${ userId} ` ;
219+ },
220+
221+ // Conditional caching
222+ shouldCache: (ctx , res ) => {
223+ // Only cache successful responses
224+ if (res .status < 200 || res .status >= 300 ) return false ;
225+
226+ // Don't cache if response is too large
227+ const size = res .headers .get (" content-length" );
228+ if (size && parseInt (size) > 1024 * 1024 ) return false ; // 1MB limit
229+
230+ return true ;
231+ },
232+
233+ // Vary cache by headers
234+ varyHeaders: [" accept" , " accept-encoding" , " accept-language" ],
235+
236+ // Respect Cache-Control headers
237+ respectCacheControl: true ,
238+
239+ // Path filtering
240+ excludePaths: [" /api/auth" , " /api/admin" , / ^ \/ ws/ ],
241+ includePaths: [" /api/public" , " /api/products" ],
242+ })
243+ );
244+
245+ // Conditional requests (ETag support)
246+ app .get (" /api/data" , cache ({ ttl: 3600 }), async (ctx ) => {
247+ const data = await getExpensiveData ();
248+ return ctx .json (data);
249+ // Middleware automatically generates ETag and handles If-None-Match
250+ });
251+
252+ // Stale-while-revalidate pattern
253+ app .use (
254+ cache ({
255+ ttl: 60 , // Fresh for 1 minute
256+ staleWhileRevalidate: true ,
257+ maxStaleAge: 3600 , // Serve stale for up to 1 hour while revalidating
258+ })
259+ );
260+
261+ // Cache invalidation
262+ import { cacheUtils } from " @rabbit-company/web-middleware" ;
263+
264+ // Invalidate specific cache keys
265+ app .post (
266+ " /api/posts" ,
267+ createPost,
268+ cacheUtils .invalidate ({
269+ storage: cache .storage ,
270+ keys: [" GET:/api/posts" , " GET:/api/posts/latest" ],
271+ })
272+ );
273+
274+ // Pattern-based invalidation (Redis only)
275+ app .put (
276+ " /api/posts/:id" ,
277+ updatePost,
278+ cacheUtils .invalidate ({
279+ storage: redisCache,
280+ patterns: [" GET:/api/posts/*" , " GET:/api/users/*/posts" ],
281+ })
282+ );
283+
284+ // Clear entire cache
285+ await cacheUtils .clear (cache .storage );
286+
287+ // Get cache statistics
288+ const stats = await cacheUtils .stats (cache .storage );
289+ console .log (` Cache size: ${ stats .size } entries` );
290+
291+ // Custom storage implementation
292+ class CustomStorage {
293+ async get (key ) {
294+ /* ... */
295+ }
296+ async set (key , entry , ttl , maxStaleAge ) {
297+ /* ... */
298+ }
299+ async delete (key ) {
300+ /* ... */
301+ }
302+ async clear () {
303+ /* ... */
304+ }
305+ async has (key ) {
306+ /* ... */
307+ }
308+ async size () {
309+ /* ... */
310+ }
311+ }
312+
313+ app .use (cache ({ storage: new CustomStorage () }));
314+ ` ` `
315+
316+ #### Storage Backends:
317+
318+ - **MemoryCache**: In-memory storage with automatic expiry
319+ - **LRUCache**: Least Recently Used eviction when size limit reached
320+ - **RedisCache**: Distributed caching with Redis
321+
322+ #### Options:
323+
324+ - ` storage` : Cache storage backend (default: MemoryCache)
325+ - ` ttl` : Time to live in seconds (default: 300)
326+ - ` methods` : HTTP methods to cache (default: ["GET", "HEAD"])
327+ - ` keyGenerator` : Custom cache key generation function
328+ - ` shouldCache` : Function to determine if response should be cached
329+ - ` varyHeaders` : Headers to include in cache key (default: ["accept", "accept-encoding"])
330+ - ` respectCacheControl` : Honor Cache-Control headers (default: true)
331+ - ` addCacheHeader` : Add X-Cache-Status header (default: true)
332+ - ` cacheHeaderName` : Custom cache status header name (default: "x-cache-status")
333+ - ` cachePrivate` : Cache private responses (default: false)
334+ - ` excludePaths` : Paths to exclude from caching
335+ - ` includePaths` : Only cache these paths (if set)
336+ - ` staleWhileRevalidate` : Serve stale content while revalidating (default: false)
337+ - ` maxStaleAge` : Maximum stale age in seconds (default: 86400)
338+
339+ #### Features:
340+
341+ - Automatic ETag generation and conditional request handling
342+ - Stale-while-revalidate for better performance
343+ - Cache-Control header support
344+ - Vary header support for content negotiation
345+ - Pattern-based invalidation with Redis
346+ - Background revalidation for stale content
347+ - Distributed caching with Redis backend
348+ - Memory efficient with configurable storage limits
349+
176350### Bearer Auth
177351
178352Token-based authentication for APIs.
0 commit comments