Xây dựng API hiệu năng cao với Cache và Interceptor trong NestJS

3 min read

🚀 Giới thiệu

Hiệu năng là yếu tố sống còn của mọi ứng dụng backend. Một API chậm không chỉ làm giảm trải nghiệm người dùng mà còn tốn tài nguyên hệ thống.
NestJS cung cấp sẵn công cụ mạnh mẽ là CacheModuleInterceptor, giúp bạn tăng tốc API dễ dàng mà không cần thay đổi logic kinh doanh.

Trong bài viết này, bạn sẽ học cách:

  • Cấu hình Redis cache trong NestJS.
  • Tạo Cache Interceptor tùy chỉnh.
  • Sử dụng cache hiệu quả với TTL.
  • Làm sạch cache khi dữ liệu thay đổi.

🧩 Cache là gì và khi nào nên dùng?

Cache là cơ chế lưu trữ tạm thời dữ liệu để truy cập nhanh hơn trong lần gọi tiếp theo.
Khi một API phải truy vấn cơ sở dữ liệu hoặc dịch vụ ngoài nhiều lần, việc cache kết quả giúp giảm tải đáng kể.

📈 Khi nào nên dùng cache

  • API đọc nhiều – ghi ít (ví dụ: danh sách sản phẩm, thống kê).
  • Dữ liệu ít thay đổi trong thời gian ngắn.
  • Truy vấn tốn tài nguyên (nhiều phép join hoặc tính toán phức tạp).

🧱 Cấu hình Cache trong NestJS

1️⃣ Cài đặt Redis

npm install cache-manager ioredis cache-manager-ioredis-yet

2️⃣ Đăng ký CacheModule

// app.module.ts
import { CacheModule, Module } from '@nestjs/common';
import { redisStore } from 'cache-manager-ioredis-yet';

@Module({
  imports: [
    CacheModule.registerAsync({
      useFactory: async () => ({
        store: await redisStore({
          host: 'localhost',
          port: 6379,
          ttl: 10, // thời gian cache mặc định 10 giây
        }),
      }),
      isGlobal: true,
    }),
  ],
})
export class AppModule {}

⚡ Nếu bạn chưa có Redis, xem hướng dẫn Redis Quick Start.


🧠 Sử dụng Cache Interceptor

NestJS có sẵn CacheInterceptor, bạn có thể áp dụng trực tiếp ở mức global hoặc cho từng route.

Áp dụng toàn cục

// main.ts
import { CacheInterceptor } from '@nestjs/cache-manager';
import { APP_INTERCEPTOR } from '@nestjs/core';

app.useGlobalInterceptors(new CacheInterceptor(app.get(CacheModule)));

Áp dụng cho controller

@UseInterceptors(CacheInterceptor)
@Controller('products')
export class ProductController {
  @Get()
  findAll() {
    return this.productService.findAll();
  }
}

Lần đầu request sẽ truy vấn DB, nhưng lần sau sẽ lấy từ cache → tốc độ nhanh hơn đáng kể.


🧩 Tạo Cache Interceptor tùy chỉnh

Đôi khi bạn muốn tùy biến key cache hoặc TTL riêng cho từng API.

@Injectable()
export class CustomCacheInterceptor extends CacheInterceptor {
  trackBy(context: ExecutionContext): string | undefined {
    const request = context.switchToHttp().getRequest();
    return `custom_cache:${request.url}`;
  }

  protected override async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
    const response$ = await super.intercept(context, next);
    console.log('✅ Cached response triggered');
    return response$;
  }
}

Áp dụng:

@UseInterceptors(CustomCacheInterceptor)
@Get(':id')
async getProduct(@Param('id') id: string) {
  return this.productService.findOne(id);
}

🔁 Làm sạch cache khi dữ liệu thay đổi

Cache cần được làm mới khi dữ liệu thay đổi.
Ví dụ, khi bạn thêm hoặc xóa sản phẩm, cache cũ không còn đúng nữa.

async createProduct(data: CreateProductDto) {
  const product = await this.repo.save(data);
  await this.cacheManager.reset(); // Xóa cache toàn bộ
  return product;
}

Hoặc bạn có thể chỉ xóa cache theo key:

await this.cacheManager.del('custom_cache:/products');

✅ Ưu điểm khi dùng Cache và Interceptor

Lợi íchMô tả
Tăng tốc APIGiảm thời gian phản hồi đáng kể.
Giảm tải databaseHạn chế truy vấn trùng lặp.
Tách biệt logicKhông cần thay đổi code service.
Dễ mở rộngCó thể áp dụng cho từng module cụ thể.

⚠️ Lỗi thường gặp

Lỗi phổ biếnGiải pháp
Redis không kết nối đượcKiểm tra cổng 6379 hoặc config trong CacheModule.
Dữ liệu cũ không bị xóaDùng TTL ngắn hoặc reset cache thủ công khi ghi dữ liệu.
Cache không hoạt độngĐảm bảo CacheInterceptor được đăng ký chính xác.
Key cache bị trùngSử dụng trackBy() để tạo key riêng.

🔗 Tham khảo thêm


💬 Kết luận

CacheInterceptor là cặp công cụ tuyệt vời giúp bạn xây dựng API hiệu năng cao mà vẫn giữ cấu trúc sạch.
Hơn nữa, chúng cho phép tách biệt logic nghiệp vụ và tối ưu hệ thống một cách an toàn.
Vì vậy, nếu bạn muốn ứng dụng NestJS của mình phản hồi nhanh hơn gấp nhiều lần — hãy bắt đầu triển khai cache ngay hôm nay!

Avatar photo

Leave a Reply

Your email address will not be published. Required fields are marked *