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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | 1x 1x 1x 1x 1x 1x 1x 1x 15x 15x 14x 14x 14x 3x 11x 1x 10x 10x 10x 10x 3x 7x 7x 6x 1x 1x 1x 1x | import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
Inject,
} from '@nestjs/common';
import { Observable, NEVER } from 'rxjs';
import { tap } from 'rxjs/operators';
import { Response, Request } from 'express';
import { ETagCacheService } from '../cache/etag-cache.service';
import { Reflector } from '@nestjs/core';
export const CACHE_TTL_KEY = 'cache_ttl';
export const CACHE_KEY_KEY = 'cache_key';
/**
* Interceptor that caches GET responses with ETag support.
* Works in conjunction with CacheMiddleware:
* - Middleware checks cache BEFORE handler and returns early if cached
* - Interceptor caches responses AFTER handler executes (for new data)
*/
@Injectable()
export class ETagCacheInterceptor implements NestInterceptor {
constructor(
@Inject(ETagCacheService) private cacheService: ETagCacheService,
private reflector: Reflector,
) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const req = context.switchToHttp().getRequest<Request>();
const res = context.switchToHttp().getResponse<Response>();
// Only cache GET requests
if (req.method !== 'GET') {
return next.handle();
}
// Check if middleware already sent a cached response
if (res.headersSent) {
return NEVER;
}
// Get cache key from response locals (set by middleware) or generate
const cacheKey = res.locals.cacheKey || req.url;
// Get custom TTL from decorator (default 30s)
// Note: TTL is retrieved for future use but cache service handles TTL internally
const _ttl =
this.reflector.getAllAndOverride<number>(CACHE_TTL_KEY, [
context.getHandler(),
context.getClass(),
]) ?? 30 * 1000;
// Execute handler and cache result
return next.handle().pipe(
tap((data) => {
// Skip if headers already sent or no data
if (res.headersSent || data === undefined || data === null) {
return;
}
// Cache the response
const etag = this.cacheService.set(cacheKey, data);
// Set ETag header if caching succeeded and headers not sent
if (etag && !res.headersSent) {
res.set('ETag', etag);
}
}),
);
}
}
// Decorators for custom cache configuration
import { SetMetadata } from '@nestjs/common';
export function CacheTTL(ttlMs: number) {
return SetMetadata(CACHE_TTL_KEY, ttlMs);
}
export function CacheKey(key: string) {
return SetMetadata(CACHE_KEY_KEY, key);
}
|