Careeroza
Caching and Timeout
A Timeout Interceptor
src/common/interceptors/timeout.interceptor.ts
import { CallHandler, ExecutionContext, Injectable, NestInterceptor, RequestTimeoutException } from '@nestjs/common';
import { Observable, TimeoutError, catchError, throwError, timeout } from 'rxjs';
@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
intercept(_context: ExecutionContext, next: CallHandler): Observable<unknown> {
return next.handle().pipe(
timeout(5000),
catchError((err) =>
throwError(() => (err instanceof TimeoutError ? new RequestTimeoutException('Took too long') : err)),
),
);
}
}Example
The same race between the handler and a timer, using promises. The slow call loses to a 100 ms limit:
class RequestTimeoutException extends Error {}
function withTimeout<T>(work: Promise<T>, ms: number): Promise<T> {
let timer: NodeJS.Timeout | undefined;
const limit = new Promise<never>((_, reject) => {
timer = setTimeout(() => reject(new RequestTimeoutException('408 Took too long')), ms);
});
return Promise.race([work, limit]).finally(() => clearTimeout(timer));
}
const delay = <T>(ms: number, value: T) => new Promise<T>((resolve) => setTimeout(() => resolve(value), ms));
async function main() {
console.log(await withTimeout(delay(10, 'fast product list'), 100));
try {
await withTimeout(delay(300, 'slow report'), 100);
} catch (e) {
console.log((e as Error).message);
}
}
main();Output
fast product list 408 Took too long
A Caching Interceptor
@nestjs/cache-manager gives you CacheInterceptor, which caches GET responses by URL. (Redis setup is in the advanced level.)
Example
npm i @nestjs/cache-manager cache-managerExample output
added 4 packages in 3s
import { CacheInterceptor, CacheKey, CacheModule, CacheTTL } from '@nestjs/cache-manager';
import { Controller, Get, Module, UseInterceptors } from '@nestjs/common';
@Controller('categories')
@UseInterceptors(CacheInterceptor)
export class CategoriesController {
@Get()
@CacheTTL(60_000) // milliseconds in cache-manager v5+
findAll() { return this.categories.findAll(); }
}
@Module({
imports: [CacheModule.register({ ttl: 30_000 })],
controllers: [CategoriesController],
})
export class CategoriesModule {}Note: Only cache data that is the same for every user. Never cache /me or /cart by URL alone, or one user may see another user's data.