Bird
Raised Fist0
NestJSframework~15 mins

Cache interceptor in NestJS - Deep Dive

Choose your learning style10 modes available
Overview - Cache interceptor
What is it?
A cache interceptor in NestJS is a special piece of code that automatically saves the results of certain operations so they can be quickly reused later. It works by catching the response from a function and storing it in a cache. When the same request happens again, it returns the saved result instead of running the function again. This helps make applications faster and reduces unnecessary work.
Why it matters
Without caching, every request would make the server do the same work repeatedly, which slows down the app and wastes resources. Cache interceptors solve this by remembering answers to common questions, so the server can respond instantly next time. This improves user experience and saves money on computing power.
Where it fits
Before learning cache interceptors, you should understand basic NestJS concepts like controllers, services, and middleware. After mastering cache interceptors, you can explore advanced caching strategies, distributed caches, and performance optimization techniques.
Mental Model
Core Idea
A cache interceptor automatically saves and reuses function results to speed up repeated requests without extra work.
Think of it like...
It's like a waiter remembering your favorite order so next time you visit, they bring it immediately without asking again.
┌───────────────┐       ┌───────────────┐       ┌───────────────┐
│ Incoming      │──────▶│ Cache         │──────▶│ Return cached │
│ Request       │       │ Check         │       │ Response      │
└───────────────┘       └───────────────┘       └───────────────┘
         │                      │                      ▲
         │                      │                      │
         │                      │                      │
         │                      ▼                      │
         │              ┌───────────────┐             │
         └─────────────▶│ Execute       │─────────────┘
                        │ Handler       │
                        └───────────────┘
                                │
                                ▼
                      ┌─────────────────┐
                      │ Store in Cache  │
                      └─────────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding Basic Interceptors
🤔
Concept: Learn what interceptors are in NestJS and how they can modify requests and responses.
Interceptors are special classes in NestJS that sit between the incoming request and the outgoing response. They can change data, add logging, or handle errors. You create an interceptor by implementing the NestInterceptor interface and using the @Injectable decorator. Then you apply it globally or to specific routes.
Result
You can intercept and modify the flow of data in your app before it reaches the client.
Understanding interceptors is key because cache interceptors are a special kind of interceptor that saves and reuses data.
2
FoundationBasics of Caching in Web Apps
🤔
Concept: Understand what caching means and why it helps web applications perform better.
Caching means saving the result of a task so you don't have to do it again. For example, if a server calculates a result for a request, caching stores that result. Next time the same request comes, the server can send the saved result immediately. This reduces waiting time and server load.
Result
You know why caching is important and how it speeds up repeated requests.
Knowing caching basics helps you see why a cache interceptor is useful in NestJS apps.
3
IntermediateUsing NestJS Cache Interceptor
🤔Before reading on: Do you think the cache interceptor automatically caches all responses or only those you specify? Commit to your answer.
Concept: Learn how to apply the built-in CacheInterceptor in NestJS to cache responses automatically.
NestJS provides a CacheInterceptor class that you can use to cache responses. You enable caching by importing CacheModule and applying CacheInterceptor globally or on specific controllers or routes. When enabled, the interceptor checks if a cached response exists for a request and returns it. Otherwise, it runs the handler and caches the result.
Result
Your app automatically caches responses for faster repeated requests without extra code.
Knowing how to apply CacheInterceptor lets you add caching with minimal effort and improves app speed.
4
IntermediateCustomizing Cache Behavior
🤔Before reading on: Can you guess if you can control cache duration and keys with CacheInterceptor? Commit to your answer.
Concept: Explore how to customize cache keys and expiration times to control caching behavior.
You can customize caching by providing options to CacheModule, like ttl (time to live) to set how long data stays cached. Also, you can create a custom cache key by overriding the trackBy method in a custom interceptor. This helps cache different responses separately based on request details.
Result
You control what gets cached and for how long, making caching smarter and more efficient.
Customizing cache keys and TTL prevents stale data and cache collisions in real apps.
5
IntermediateCombining Cache with Other Interceptors
🤔Before reading on: Do you think cache interceptors run before or after logging interceptors? Commit to your answer.
Concept: Understand how cache interceptors interact with other interceptors and the order of execution.
Interceptors run in the order they are applied. CacheInterceptor usually runs early to return cached data quickly. Other interceptors like logging or transformation run after. You can control order by applying interceptors at different levels or stacking them carefully.
Result
You can design interceptor chains that work well together without conflicts.
Knowing interceptor order helps avoid bugs where cache returns outdated data or logs miss requests.
6
AdvancedImplementing Custom Cache Interceptor
🤔Before reading on: Do you think you can write your own cache interceptor to add features not in the built-in one? Commit to your answer.
Concept: Learn how to create a custom cache interceptor to add special caching logic or integrate with external caches.
You can create a custom interceptor by extending CacheInterceptor or implementing NestInterceptor. This lets you add features like conditional caching, logging cache hits/misses, or using Redis or other stores. You override intercept method to control caching flow.
Result
You build flexible caching tailored to your app's needs beyond default behavior.
Custom interceptors unlock advanced caching strategies and integration with external systems.
7
ExpertCache Interceptor Internals and Pitfalls
🤔Before reading on: Do you think cache interceptor caches errors or only successful responses? Commit to your answer.
Concept: Dive into how CacheInterceptor works internally and common pitfalls like caching errors or stale data.
CacheInterceptor uses RxJS to handle async responses. It caches only successful responses by default. It uses request URL and parameters as cache keys unless customized. Pitfalls include caching sensitive data, stale cache if TTL is too long, or cache stampede when many requests miss cache simultaneously.
Result
You understand how cache interceptor works under the hood and how to avoid common bugs.
Knowing internals helps you write safer, more reliable caching and troubleshoot issues quickly.
Under the Hood
CacheInterceptor in NestJS wraps the handler function and uses RxJS operators to capture the response stream. When a request comes, it checks the cache store for a matching key. If found, it returns the cached Observable immediately. If not, it calls the handler, caches the emitted response, and then returns it. The cache store can be in-memory or external like Redis. The interceptor uses request details to generate cache keys and respects TTL settings to expire cache entries.
Why designed this way?
NestJS designed CacheInterceptor to integrate seamlessly with its reactive programming model using RxJS. This allows caching asynchronous responses naturally. Using interceptors fits the framework's modular design, letting developers add caching without changing business logic. The design balances ease of use with flexibility by allowing customization of cache keys and stores.
┌───────────────┐
│ Incoming      │
│ Request       │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Cache Check   │
│ (Cache Store) │
└──────┬────────┘
       │ Cache Hit?
   Yes │ / No
       ▼    ┌───────────────┐
┌───────────┐│ Call Handler  │
│ Return    ││ Function      │
│ Cached    │└──────┬────────┘
│ Response  │       │
└───────────┘       ▼
                ┌───────────────┐
                │ Cache Response │
                │ Store Entry   │
                └──────┬────────┘
                       │
                       ▼
                ┌───────────────┐
                │ Return Result │
                └───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does CacheInterceptor cache error responses by default? Commit to yes or no.
Common Belief:CacheInterceptor caches every response including errors to speed up all requests.
Tap to reveal reality
Reality:CacheInterceptor caches only successful responses by default and does not cache errors.
Why it matters:Caching errors would cause clients to get wrong data repeatedly and hide real issues.
Quick: Is cache key always just the URL string? Commit to yes or no.
Common Belief:Cache keys are always the request URL string, so different queries with same URL share cache.
Tap to reveal reality
Reality:Cache keys include URL plus query parameters and can be customized to avoid collisions.
Why it matters:Wrong cache keys cause wrong data to be returned for different requests, breaking app correctness.
Quick: Does CacheInterceptor automatically clear cache when data changes? Commit to yes or no.
Common Belief:CacheInterceptor automatically updates or clears cache when underlying data changes.
Tap to reveal reality
Reality:CacheInterceptor does not know about data changes; cache invalidation must be handled separately.
Why it matters:Without manual invalidation, users may see outdated data causing confusion or errors.
Quick: Can CacheInterceptor be used with any kind of response data? Commit to yes or no.
Common Belief:CacheInterceptor works perfectly with all response types including streams and file downloads.
Tap to reveal reality
Reality:CacheInterceptor works best with JSON or simple data; caching streams or files requires custom handling.
Why it matters:Misusing cache interceptor with unsupported data types can cause errors or corrupted responses.
Expert Zone
1
CacheInterceptor uses RxJS shareReplay operator internally to multicast cached responses, which can cause memory leaks if not managed carefully.
2
Custom cache keys can include user identity or headers to support personalized caching, but this increases cache complexity and size.
3
Cache invalidation strategies like time-based TTL or manual clearing are critical; naive caching leads to stale data and bugs.
When NOT to use
Avoid using CacheInterceptor for highly dynamic data that changes frequently or for sensitive data that must not be stored. Instead, use manual caching strategies or no caching. For large binary data or streaming, use specialized caching or CDN solutions.
Production Patterns
In production, CacheInterceptor is often combined with Redis or Memcached stores for distributed caching. Developers implement layered caching with short TTLs and manual invalidation on data updates. Cache keys are carefully designed to include query params and user context. Monitoring cache hit rates and errors is standard practice.
Connections
HTTP Caching Headers
Builds-on
Understanding HTTP caching headers like ETag and Cache-Control helps design cache interceptors that respect client-side caching and reduce server load.
Memoization in Functional Programming
Same pattern
Cache interceptors are like memoization, where function results are saved to avoid repeated work, showing a shared principle across programming styles.
Human Memory Recall
Builds-on
Cache interceptors mimic how human memory recalls past experiences quickly instead of reprocessing every detail, illustrating a natural efficiency pattern.
Common Pitfalls
#1Caching error responses causing wrong data delivery.
Wrong approach:class MyCacheInterceptor extends CacheInterceptor { intercept(context, next) { return next.handle().pipe( tap(response => this.cacheManager.set('key', response)) ); } }
Correct approach:class MyCacheInterceptor extends CacheInterceptor { intercept(context, next) { return next.handle().pipe( tap(response => { if (response && !response.error) { this.cacheManager.set('key', response); } }) ); } }
Root cause:Not checking if the response is an error before caching leads to storing invalid data.
#2Using default cache keys ignoring query parameters.
Wrong approach:CacheInterceptor caches using only request URL without query params, causing collisions.
Correct approach:Override trackBy method to include query params in cache key: trackBy(context) { const request = context.switchToHttp().getRequest(); return request.url + JSON.stringify(request.query); }
Root cause:Assuming URL alone uniquely identifies requests ignores important request details.
#3Not invalidating cache after data updates.
Wrong approach:Relying solely on TTL without clearing cache when data changes.
Correct approach:Manually clear cache entries after data updates using cacheManager.del(key).
Root cause:Believing cache automatically updates leads to stale data being served.
Key Takeaways
Cache interceptors in NestJS automatically save and reuse responses to speed up repeated requests.
They work by intercepting requests and checking a cache store before running the handler function.
Customizing cache keys and expiration times is essential to avoid stale or incorrect data.
Understanding interceptor order and RxJS usage helps avoid common caching bugs.
Advanced use includes custom interceptors and integration with external cache stores like Redis.

Practice

(1/5)
1. What is the main purpose of the CacheInterceptor in NestJS?
easy
A. To handle authentication and authorization
B. To store responses and speed up repeated requests
C. To log all incoming requests
D. To format response data before sending

Solution

  1. Step 1: Understand the role of CacheInterceptor

    The CacheInterceptor caches HTTP responses to avoid recomputing them on repeated requests.
  2. Step 2: Compare with other options

    Logging, authentication, and formatting are handled by other interceptors or middleware, not CacheInterceptor.
  3. Final Answer:

    To store responses and speed up repeated requests -> Option B
  4. Quick Check:

    CacheInterceptor caches responses = C [OK]
Hint: CacheInterceptor caches responses to speed up requests [OK]
Common Mistakes:
  • Confusing CacheInterceptor with logging interceptors
  • Thinking it handles security tasks
  • Assuming it formats data
2. Which of the following is the correct way to apply the CacheInterceptor to a controller method in NestJS?
easy
A. Add CacheInterceptor in the constructor of the controller
B. @Cacheable() decorator on the method
C. @UseInterceptors(CacheInterceptor) above the method
D. Use @CacheInterceptor() as a method parameter

Solution

  1. Step 1: Recall how to apply interceptors in NestJS

    Interceptors are applied using the @UseInterceptors() decorator above methods or controllers.
  2. Step 2: Check each option

    Only @UseInterceptors(CacheInterceptor) above the method uses the correct decorator syntax. Options B, C, and D are invalid or do not exist in NestJS.
  3. Final Answer:

    @UseInterceptors(CacheInterceptor) above the method -> Option C
  4. Quick Check:

    UseInterceptors decorator = A [OK]
Hint: Use @UseInterceptors(CacheInterceptor) to apply caching [OK]
Common Mistakes:
  • Using non-existent @Cacheable decorator
  • Trying to inject interceptor in constructor
  • Passing interceptor as method parameter
3. Given this code snippet, what will be the output on the second request to /hello?
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { CacheInterceptor } from '@nestjs/cache-manager';

@Controller()
export class HelloController {
  private count = 0;

  @UseInterceptors(CacheInterceptor)
  @Get('hello')
  getHello() {
    this.count++;
    return `Hello count: ${this.count}`;
  }
}
medium
A. "Hello count: 1" first request, "Hello count: 2" second request
B. Error because count is private
C. "Hello count: 0" on both requests
D. "Hello count: 1" on first and second request

Solution

  1. Step 1: Understand CacheInterceptor behavior

    CacheInterceptor caches the response of the first request and returns it for subsequent requests without calling the method again.
  2. Step 2: Analyze the count variable increment

    On the first request, count increments to 1 and returns "Hello count: 1". On the second request, the cached response is returned, so count does not increment again.
  3. Final Answer:

    "Hello count: 1" on first and second request -> Option D
  4. Quick Check:

    Cache returns cached response = B [OK]
Hint: CacheInterceptor returns cached response, method not re-executed [OK]
Common Mistakes:
  • Assuming count increments on every request
  • Thinking private variables cause errors here
  • Ignoring caching effect on method execution
4. Identify the error in this NestJS CacheInterceptor usage:
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { CacheInterceptor } from '@nestjs/cache-manager';

@Controller()
export class TestController {
  @UseInterceptors(CacheInterceptor())
  @Get('test')
  test() {
    return 'Test response';
  }
}
medium
A. CacheInterceptor should not be called as a function
B. Missing CacheModule import in the controller
C. Method test must be async to use CacheInterceptor
D. CacheInterceptor must be applied globally, not per method

Solution

  1. Step 1: Check how CacheInterceptor is applied

    CacheInterceptor is a class, not a function, so it should be passed without parentheses to @UseInterceptors().
  2. Step 2: Verify other options

    CacheModule import is required but not shown here (not an error in this snippet). Method does not need to be async. CacheInterceptor can be applied per method.
  3. Final Answer:

    CacheInterceptor should not be called as a function -> Option A
  4. Quick Check:

    Use class name without () = A [OK]
Hint: Pass CacheInterceptor class, not call it as function [OK]
Common Mistakes:
  • Calling CacheInterceptor with parentheses
  • Assuming method must be async
  • Thinking CacheModule import error here
5. You want to customize the cache key for a route in NestJS using CacheInterceptor. Which approach correctly achieves this?
hard
A. Extend CacheInterceptor and override the trackBy method to return a custom key
B. Pass a custom key string directly to @UseInterceptors(CacheInterceptor)
C. Set a custom key in the controller constructor
D. Use @CacheKey('customKey') decorator on the method

Solution

  1. Step 1: Understand how to customize cache keys

    CacheInterceptor allows customization by extending it and overriding the trackBy method to define how keys are generated.
  2. Step 2: Evaluate other options

    Passing a string to @UseInterceptors() is invalid. Setting keys in constructor or using @CacheKey decorator does not exist in NestJS.
  3. Final Answer:

    Extend CacheInterceptor and override the trackBy method to return a custom key -> Option A
  4. Quick Check:

    Override trackBy method = D [OK]
Hint: Override trackBy method in extended CacheInterceptor [OK]
Common Mistakes:
  • Trying to pass key directly to decorator
  • Using non-existent @CacheKey decorator
  • Setting keys in constructor instead of method
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard