# NestJS và Redis năm 2026: Caching, Sessions và Câu hỏi Phỏng vấn > Hướng dẫn toàn diện về tích hợp NestJS với Redis cho caching và quản lý session, kèm theo các câu hỏi phỏng vấn để chuẩn bị cho buổi phỏng vấn Node.js. - Published: 2026-09-10 - Updated: 2026-09-10 - Author: Anthony Fillion-Maillet - Tags: nestjs, redis, caching, nodejs, interview - Reading time: 12 min --- Tích hợp NestJS với Redis cải thiện đáng kể hiệu năng ứng dụng bằng cách thay thế các truy vấn database bằng việc tìm kiếm trong bộ nhớ. Sự kết hợp giữa @nestjs/cache-manager cho caching và express-session với connect-redis cho việc lưu trữ session bao quát hai trường hợp sử dụng Redis phổ biến nhất trong các ứng dụng backend. > **Điểm Quan Trọng** > > Caching Redis trong NestJS giảm tải database bằng cách lưu trữ dữ liệu thường xuyên truy cập trong bộ nhớ. Quản lý session với Redis cho phép horizontal scaling bằng cách chia sẻ trạng thái session giữa nhiều instance. ## Thiết lập @nestjs/cache-manager với Redis Module cache-manager được chuyển sang package riêng từ NestJS 10. Việc cài đặt yêu cầu cả wrapper NestJS và cache store nền tảng. Adapter @keyv/redis là phương pháp được khuyến nghị cho việc tích hợp Redis với cache-manager v5. ```bash # Cài đặt các package cần thiết npm install @nestjs/cache-manager cache-manager @keyv/redis ``` Việc đăng ký module cấu hình kết nối Redis và TTL mặc định. Tùy chọn `isGlobal` làm cho cache có sẵn trên tất cả các module mà không cần import lại. ```typescript // app.module.ts import { Module } from '@nestjs/common'; import { CacheModule } from '@nestjs/cache-manager'; import KeyvRedis from '@keyv/redis'; @Module({ imports: [ CacheModule.registerAsync({ isGlobal: true, useFactory: () => ({ stores: [ new KeyvRedis(process.env.REDIS_URL || 'redis://localhost:6379'), ], ttl: 60000, // TTL mặc định tính bằng mili giây }), }), ], }) export class AppModule {} ``` Mảng `stores` chấp nhận nhiều cache store cho multi-tier caching. Môi trường production thường sử dụng Redis làm store chính, với fallback in-memory tùy chọn cho development. ## Cache Injection và Service-Level Caching Token `CACHE_MANAGER` cung cấp quyền truy cập trực tiếp vào cache cho các hoạt động ở cấp service. Phương pháp này cung cấp nhiều kiểm soát hơn so với HTTP caching tự động khi business logic quyết định thời điểm invalidate cache. ```typescript // users.service.ts import { Injectable, Inject } from '@nestjs/common'; import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager'; import { UsersRepository } from './users.repository'; import { User } from './user.entity'; @Injectable() export class UsersService { constructor( @Inject(CACHE_MANAGER) private cache: Cache, private usersRepository: UsersRepository, ) {} async findById(id: string): Promise { // Kiểm tra cache trước const cacheKey = `user:${id}`; const cached = await this.cache.get(cacheKey); if (cached) { return cached; } // Cache miss: lấy từ database const user = await this.usersRepository.findById(id); if (user) { await this.cache.set(cacheKey, user, 300000); // TTL 5 phút } return user; } async update(id: string, data: Partial): Promise { const user = await this.usersRepository.update(id, data); // Invalidate cache sau khi update await this.cache.del(`user:${id}`); return user; } async delete(id: string): Promise { await this.usersRepository.delete(id); await this.cache.del(`user:${id}`); } } ``` Mẫu cache-aside được triển khai ở trên kiểm tra cache trước khi truy vấn database. Việc invalidation xảy ra trong các hoạt động write để duy trì tính nhất quán dữ liệu giữa cache và nguồn dữ liệu gốc. ## HTTP Response Caching với Interceptor NestJS cung cấp `CacheInterceptor` để tự động cache các phản hồi HTTP. Các decorator `@CacheTTL()` và `@CacheKey()` tùy chỉnh hành vi cho từng endpoint. ```typescript // products.controller.ts import { Controller, Get, Param, UseInterceptors } from '@nestjs/common'; import { CacheInterceptor, CacheTTL, CacheKey } from '@nestjs/cache-manager'; import { ProductsService } from './products.service'; @Controller('products') @UseInterceptors(CacheInterceptor) export class ProductsController { constructor(private productsService: ProductsService) {} @Get() @CacheTTL(120000) // 2 phút cho danh sách sản phẩm async findAll() { return this.productsService.findAll(); } @Get(':id') @CacheTTL(300000) // 5 phút cho sản phẩm riêng lẻ async findOne(@Param('id') id: string) { return this.productsService.findById(id); } @Get('category/:category') @CacheKey('products-by-category') @CacheTTL(180000) async findByCategory(@Param('category') category: string) { return this.productsService.findByCategory(category); } } ``` Interceptor tự động cache các phản hồi GET sử dụng URL request làm key mặc định. Custom cache key hữu ích khi nhiều route cần chia sẻ hoặc có các entry cache khác nhau. ## Quản lý Session với Redis Store Session HTTP được hỗ trợ bởi Redis cho phép dữ liệu session tồn tại qua các lần khởi động lại server và horizontal scaling. Package express-session tích hợp với NestJS thông qua middleware. ```bash npm install express-session connect-redis ioredis npm install -D @types/express-session ``` Cấu hình session store yêu cầu một instance Redis client. Package ioredis cung cấp hỗ trợ Redis tốt hơn với clustering và sentinel. ```typescript // main.ts import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import * as session from 'express-session'; import RedisStore from 'connect-redis'; import { Redis } from 'ioredis'; async function bootstrap() { const app = await NestFactory.create(AppModule); const redisClient = new Redis( process.env.REDIS_URL || 'redis://localhost:6379' ); app.use( session({ store: new RedisStore({ client: redisClient }), secret: process.env.SESSION_SECRET || 'your-secret-key', resave: false, saveUninitialized: false, cookie: { secure: process.env.NODE_ENV === 'production', httpOnly: true, maxAge: 24 * 60 * 60 * 1000, // 24 giờ sameSite: 'lax', }, }), ); await app.listen(3000); } bootstrap(); ``` Tùy chọn `resave: false` ngăn session được lưu lại nếu không bị thay đổi. Cài đặt `saveUninitialized: false` đảm bảo các session trống không làm đầy bộ nhớ Redis. ## Truy cập Session Data trong Controller Dữ liệu session có sẵn thông qua đối tượng request. Custom decorator đơn giản hóa việc truy cập session trên các handler. ```typescript // session.decorator.ts import { createParamDecorator, ExecutionContext } from '@nestjs/common'; export const GetSession = createParamDecorator( (data: string, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); return data ? request.session?.[data] : request.session; }, ); // auth.controller.ts import { Controller, Post, Body, Get } from '@nestjs/common'; import { GetSession } from './session.decorator'; import { Session } from 'express-session'; interface UserSession extends Session { userId?: string; role?: string; loginAt?: Date; } @Controller('auth') export class AuthController { @Post('login') async login( @Body() credentials: LoginDto, @GetSession() session: UserSession, ) { // Xác thực thông tin đăng nhập const user = await this.authService.validate(credentials); // Lưu dữ liệu user vào session session.userId = user.id; session.role = user.role; session.loginAt = new Date(); return { message: 'Đăng nhập thành công' }; } @Post('logout') async logout(@GetSession() session: UserSession) { return new Promise((resolve, reject) => { session.destroy((err) => { if (err) reject(err); resolve({ message: 'Đăng xuất thành công' }); }); }); } @Get('profile') async getProfile(@GetSession('userId') userId: string) { if (!userId) { throw new UnauthorizedException('Session không hợp lệ'); } return this.usersService.findById(userId); } } ``` ## Chiến lược Invalidate Cache Chiến lược invalidate cache phù hợp duy trì tính nhất quán dữ liệu. Các mẫu phổ biến bao gồm time-based expiration, event-driven invalidation và manual purging. ```typescript // cache-invalidation.service.ts import { Injectable, Inject } from '@nestjs/common'; import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager'; import { OnEvent } from '@nestjs/event-emitter'; @Injectable() export class CacheInvalidationService { constructor(@Inject(CACHE_MANAGER) private cache: Cache) {} // Event-driven invalidation @OnEvent('user.updated') async handleUserUpdate(payload: { userId: string }) { await this.cache.del(`user:${payload.userId}`); await this.cache.del('users:list'); } @OnEvent('product.created') async handleProductCreate() { // Invalidate tất cả cache sản phẩm khi sản phẩm mới được tạo await this.invalidatePattern('products:*'); } // Pattern-based invalidation yêu cầu truy cập Redis trực tiếp async invalidatePattern(pattern: string): Promise { const store = this.cache.stores[0] as any; const redis = store?.opts?.redis || store?.redis; if (redis) { const keys = await redis.keys(pattern); if (keys.length > 0) { await redis.del(...keys); } } } // Manual cache warming async warmCache(): Promise { const popularProducts = await this.productsService.findPopular(); for (const product of popularProducts) { await this.cache.set(`product:${product.id}`, product, 600000); } } } ``` ## Rate Limiting với Redis Redis cung cấp bộ nhớ phân tán cho các rate limit counter. Package @nestjs/throttler hỗ trợ Redis làm backend lưu trữ. ```bash npm install @nestjs/throttler @nestjs/throttler-storage-redis ioredis ``` ```typescript // app.module.ts import { Module } from '@nestjs/common'; import { ThrottlerModule } from '@nestjs/throttler'; import { ThrottlerStorageRedisService } from '@nestjs/throttler-storage-redis'; import { Redis } from 'ioredis'; @Module({ imports: [ ThrottlerModule.forRootAsync({ useFactory: () => ({ throttlers: [ { name: 'short', ttl: 1000, limit: 3, }, { name: 'medium', ttl: 10000, limit: 20, }, { name: 'long', ttl: 60000, limit: 100, }, ], storage: new ThrottlerStorageRedisService( new Redis(process.env.REDIS_URL || 'redis://localhost:6379') ), }), }), ], }) export class AppModule {} ``` ## Câu hỏi Phỏng vấn: Caching và Redis trong NestJS Dưới đây là các câu hỏi phỏng vấn phổ biến để đánh giá kiến thức về tích hợp Redis trong ứng dụng NestJS. **Câu hỏi 1: Sự khác biệt giữa cache-aside và read-through caching là gì?** Cache-aside yêu cầu code ứng dụng kiểm tra cache một cách rõ ràng và điền dữ liệu khi cache miss. Read-through caching ủy quyền quá trình này cho cache layer. NestJS cache-manager sử dụng mẫu cache-aside trong đó developer chịu trách nhiệm về logic get/set. **Câu hỏi 2: Làm thế nào để xử lý cache stampede?** Cache stampede xảy ra khi nhiều request đồng thời gặp cache miss và tất cả đều truy vấn database. Các giải pháp bao gồm request coalescing (xếp hàng các request trùng lặp), probabilistic early expiration, hoặc lock-based population trong đó chỉ một request điền cache trong khi các request khác chờ đợi. **Câu hỏi 3: Tại sao sử dụng Redis cho session thay vì session dựa trên bộ nhớ?** Session dựa trên bộ nhớ không tồn tại qua các lần khởi động lại server và không thể chia sẻ giữa nhiều instance. Redis cung cấp tính bền vững, horizontal scaling và các tính năng expiration tích hợp cho quản lý session. **Câu hỏi 4: Giải thích sự khác biệt giữa cache TTL và sliding expiration?** TTL cố định hết hạn entry tại khoảng thời gian đã đặt kể từ khi tạo. Sliding expiration đặt lại bộ đếm thời gian mỗi khi dữ liệu được truy cập. Redis gốc chỉ hỗ trợ TTL cố định; sliding expiration yêu cầu logic ứng dụng để cập nhật thời gian expiration khi đọc. **Câu hỏi 5: Làm thế nào để cache dữ liệu khác nhau cho mỗi user?** Sử dụng cache key bao gồm user identifier: `user:${userId}:preferences`. Điều này đảm bảo sự cách ly giữa dữ liệu của các user khác nhau trong khi vẫn được hưởng lợi từ caching. **Câu hỏi 6: Sự đánh đổi giữa cache hit rate và memory usage là gì?** TTL cao hơn tăng hit rate nhưng tiêu thụ nhiều bộ nhớ hơn và có nguy cơ dữ liệu trở nên cũ. TTL ngắn hơn giữ dữ liệu mới nhưng tăng tải database. Giải pháp tối ưu cân bằng cả hai yếu tố dựa trên mẫu truy cập và độ nhạy cảm của dữ liệu. ## Kết luận Tích hợp Redis với NestJS yêu cầu hiểu biết về các mẫu caching, chiến lược quản lý session và các cân nhắc cấu hình. Module @nestjs/cache-manager cung cấp một abstraction rõ ràng cho các hoạt động caching, trong khi express-session với connect-redis xử lý việc lưu trữ session. Sự kết hợp của cả hai cho phép ứng dụng NestJS scale theo chiều ngang trong khi duy trì hiệu năng và quản lý trạng thái nhất quán. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/vi/blog/node-nestjs/nestjs-redis-caching-sessions-interview-questions