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.

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.
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.
# Cài đặt các package cần thiết
npm install @nestjs/cache-manager cache-manager @keyv/redisViệ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.
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.
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<User | null> {
// Kiểm tra cache trước
const cacheKey = `user:${id}`;
const cached = await this.cache.get<User>(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<User>): Promise<User> {
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<void> {
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.
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.
npm install express-session connect-redis ioredis
npm install -D @types/express-sessionCấ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.
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.
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.
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<void> {
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<void> {
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ữ.
npm install @nestjs/throttler @nestjs/throttler-storage-redis ioredisimport { 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 {}Sẵn sàng chinh phục phỏng vấn Node.js / NestJS?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
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.
Bạn có tìm ra lỗi trong Node.js / NestJS không?
Một đoạn mã thật, một lỗi ẩn, mỗi ngày một lượt. Không cần tài khoản để thử.

Viết bởi
Anthony Fillion-MailletNgười sáng lập SharpSkill
Lập trình viên fullstack hơn 10 năm. Anh điều hành SharpSkill và chịu trách nhiệm về mọi nội dung đăng tại đây.
Cập nhật ngày 10 tháng 9, 2026
Thẻ
Chia sẻ
Bài viết liên quan

NestJS và MongoDB trong 2026: Mongoose, Aggregation và Câu hỏi Phỏng vấn
Thành thạo NestJS với MongoDB và Mongoose 9. Học thiết kế schema, aggregation pipeline, và chuẩn bị phỏng vấn kỹ thuật với các ví dụ thực tế.

NestJS va WebSockets nam 2026: Real-Time, Gateway va Cau hoi Phong van
Huong dan day du ve xay dung ung dung real-time voi NestJS va WebSockets, bao gom gateway, best practices va cac cau hoi phong van ky thuat.

Microservices với NestJS năm 2026: Kiến trúc, gRPC và Câu hỏi Phỏng vấn
Hướng dẫn toàn diện về kiến trúc microservices NestJS với gRPC: transport layer, Protocol Buffers, streaming patterns và câu hỏi phỏng vấn cho backend engineer năm 2026.