Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | 1x 1x 1x 15x 15x 4x 11x 11x 11x 11x 11x 2x 9x 9x 9x 9x 3x 3x 3x 6x 6x | import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { ETagCacheService } from '../cache/etag-cache.service';
/**
* Middleware that checks ETag cache BEFORE handler execution.
* If client has matching ETag, returns 304 immediately without running handler.
* This is only for 304 optimization - cache misses still go through handler.
*
* To bypass cache from frontend, send either:
* - Cache-Control: no-cache header
* - X-Skip-Cache: true header
*/
@Injectable()
export class CacheMiddleware implements NestMiddleware {
constructor(private readonly cacheService: ETagCacheService) {}
use(req: Request, res: Response, next: NextFunction) {
// Only cache GET requests
if (req.method !== 'GET') {
return next();
}
// Check for cache bypass headers from frontend
const cacheControl = req.headers['cache-control'];
const skipCache = req.headers['x-skip-cache'];
Iif (cacheControl === 'no-cache' || skipCache === 'true') {
// Frontend explicitly requested fresh data - skip cache
return next();
}
// Check If-None-Match header for potential 304
const clientEtag = req.headers['if-none-match'];
if (!clientEtag) {
// No ETag from client - proceed to handler
return next();
}
// Generate cache key from URL and query params
const queryString =
Object.keys(req.query).length > 0 ? JSON.stringify(req.query) : '';
const cacheKey = `${req.url}:${queryString}`;
// Check for cached entry
const cached = this.cacheService.get(cacheKey);
if (cached && clientEtag === cached.etag) {
// Client has fresh data - send 304 without running handler
res.setHeader('ETag', cached.etag);
res.status(304).send();
return; // Stop - don't call next()
}
// No cache hit or mismatch - proceed to handler
// Store cache key for interceptor to use
res.locals.cacheKey = cacheKey;
next();
}
}
|