NestJS และ Redis ในปี 2026: Caching, Sessions และคำถามสัมภาษณ์
คู่มือฉบับสมบูรณ์สำหรับการผสาน NestJS กับ Redis สำหรับ caching และการจัดการ session พร้อมคำถามสัมภาษณ์เพื่อเตรียมตัวสำหรับการสัมภาษณ์งาน Node.js

การผสาน NestJS กับ Redis เปลี่ยนแปลงประสิทธิภาพของแอปพลิเคชันอย่างมากโดยการแทนที่การเรียก database ด้วยการค้นหาในหน่วยความจำ การผสมผสานระหว่าง @nestjs/cache-manager สำหรับ caching และ express-session กับ connect-redis สำหรับการเก็บรักษา session ครอบคลุมกรณีการใช้งาน Redis ที่พบบ่อยที่สุดสองกรณีในแอปพลิเคชัน backend
Redis caching ใน NestJS ลดภาระ database โดยการเก็บข้อมูลที่เข้าถึงบ่อยไว้ในหน่วยความจำ การจัดการ session ด้วย Redis ช่วยให้สามารถ horizontal scaling ได้โดยการแชร์สถานะ session ระหว่าง instance ต่างๆ
การตั้งค่า @nestjs/cache-manager กับ Redis
โมดูล cache-manager ถูกย้ายไปยัง package แยกต่างหากตั้งแต่ NestJS 10 การติดตั้งต้องการทั้ง wrapper ของ NestJS และ cache store พื้นฐาน Adapter @keyv/redis เป็นวิธีที่แนะนำสำหรับการผสาน Redis กับ cache-manager v5
# ติดตั้ง package ที่จำเป็น
npm install @nestjs/cache-manager cache-manager @keyv/redisการลงทะเบียนโมดูลกำหนดค่าการเชื่อมต่อ Redis และ TTL เริ่มต้น ตัวเลือก isGlobal ทำให้ cache พร้อมใช้งานในทุกโมดูลโดยไม่ต้อง import ซ้ำ
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 เริ่มต้นในหน่วยมิลลิวินาที
}),
}),
],
})
export class AppModule {}อาร์เรย์ stores รับ cache store หลายตัวสำหรับ multi-tier caching สภาพแวดล้อม production มักใช้ Redis เป็น store หลัก พร้อมกับ fallback in-memory ที่เป็นทางเลือกสำหรับ development
Cache Injection และ Service-Level Caching
Token CACHE_MANAGER ให้การเข้าถึง cache โดยตรงสำหรับการดำเนินการระดับ service วิธีนี้ให้การควบคุมมากกว่า HTTP caching อัตโนมัติเมื่อ business logic กำหนดว่าเมื่อใดควร 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> {
// ตรวจสอบ cache ก่อน
const cacheKey = `user:${id}`;
const cached = await this.cache.get<User>(cacheKey);
if (cached) {
return cached;
}
// Cache miss: ดึงข้อมูลจาก database
const user = await this.usersRepository.findById(id);
if (user) {
await this.cache.set(cacheKey, user, 300000); // TTL 5 นาที
}
return user;
}
async update(id: string, data: Partial<User>): Promise<User> {
const user = await this.usersRepository.update(id, data);
// Invalidate cache หลังจาก 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}`);
}
}รูปแบบ cache-aside ที่ implement ด้านบนจะตรวจสอบ cache ก่อนการ query database การ invalidation เกิดขึ้นในการดำเนินการ write เพื่อรักษาความสอดคล้องของข้อมูลระหว่าง cache และแหล่งข้อมูลหลัก
HTTP Response Caching ด้วย Interceptor
NestJS มี CacheInterceptor สำหรับการ cache response HTTP โดยอัตโนมัติ Decorator @CacheTTL() และ @CacheKey() ปรับแต่งพฤติกรรมต่อ 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 นาทีสำหรับรายการสินค้า
async findAll() {
return this.productsService.findAll();
}
@Get(':id')
@CacheTTL(300000) // 5 นาทีสำหรับสินค้าแต่ละรายการ
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 จะ cache response GET โดยอัตโนมัติโดยใช้ URL ของ request เป็น key เริ่มต้น Custom cache key มีประโยชน์เมื่อหลาย route ต้องแชร์หรือมี cache entry ที่แตกต่างกัน
การจัดการ Session ด้วย Redis Store
HTTP session ที่สนับสนุนโดย Redis อนุญาตให้ข้อมูล session คงอยู่ข้ามการรีสตาร์ทเซิร์ฟเวอร์และการ horizontal scaling Package express-session ผสานกับ NestJS ผ่าน middleware
npm install express-session connect-redis ioredis
npm install -D @types/express-sessionการกำหนดค่า session store ต้องการ instance ของ Redis client Package ioredis ให้การสนับสนุน Redis ที่ดีกว่าพร้อม clustering และ 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 ชั่วโมง
sameSite: 'lax',
},
}),
);
await app.listen(3000);
}
bootstrap();ตัวเลือก resave: false ป้องกันการบันทึก session ซ้ำหากไม่มีการแก้ไข การตั้งค่า saveUninitialized: false รับประกันว่า session ว่างเปล่าจะไม่เติมเต็มพื้นที่เก็บข้อมูล Redis
การเข้าถึง Session Data ใน Controller
ข้อมูล session พร้อมใช้งานผ่าน request object Custom decorator ทำให้การเข้าถึง session ง่ายขึ้นข้าม 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,
) {
// ตรวจสอบ credentials
const user = await this.authService.validate(credentials);
// เก็บข้อมูล user ใน session
session.userId = user.id;
session.role = user.role;
session.loginAt = new Date();
return { message: 'เข้าสู่ระบบสำเร็จ' };
}
@Post('logout')
async logout(@GetSession() session: UserSession) {
return new Promise((resolve, reject) => {
session.destroy((err) => {
if (err) reject(err);
resolve({ message: 'ออกจากระบบสำเร็จ' });
});
});
}
@Get('profile')
async getProfile(@GetSession('userId') userId: string) {
if (!userId) {
throw new UnauthorizedException('Session ไม่ถูกต้อง');
}
return this.usersService.findById(userId);
}
}กลยุทธ์การ Invalidate Cache
กลยุทธ์การ invalidate cache ที่เหมาะสมรักษาความสอดคล้องของข้อมูล รูปแบบทั่วไป ได้แก่ time-based expiration, event-driven invalidation และ 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 cache สินค้าทั้งหมดเมื่อสร้างสินค้าใหม่
await this.invalidatePattern('products:*');
}
// Pattern-based invalidation ต้องการการเข้าถึง Redis โดยตรง
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 ด้วย Redis
Redis ให้พื้นที่เก็บข้อมูลแบบกระจายสำหรับ rate limit counter Package @nestjs/throttler รองรับ Redis เป็น storage backend
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 {}พร้อมที่จะพิชิตการสัมภาษณ์ Node.js / NestJS แล้วหรือยังครับ?
ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ
คำถามสัมภาษณ์: Caching และ Redis ใน NestJS
ต่อไปนี้เป็นคำถามสัมภาษณ์ทั่วไปที่ทดสอบความรู้เกี่ยวกับการผสาน Redis ในแอปพลิเคชัน NestJS
คำถาม 1: ความแตกต่างระหว่าง cache-aside และ read-through caching คืออะไร?
Cache-aside กำหนดให้โค้ดของแอปพลิเคชันตรวจสอบ cache อย่างชัดเจนและเติมข้อมูลเมื่อเกิด cache miss Read-through caching มอบหมายกระบวนการนี้ให้กับ cache layer เอง NestJS cache-manager ใช้รูปแบบ cache-aside ที่ developer รับผิดชอบต่อ logic ของ get/set
คำถาม 2: จะจัดการกับ cache stampede ได้อย่างไร?
Cache stampede เกิดขึ้นเมื่อหลาย request พร้อมกันพบ cache miss และทั้งหมด query database วิธีแก้ไขรวมถึง request coalescing (การจัดคิว request ที่ซ้ำกัน), probabilistic early expiration, หรือ lock-based population ที่มีเพียง request เดียวที่เติม cache ในขณะที่ request อื่นรอ
คำถาม 3: ทำไมต้องใช้ Redis สำหรับ session แทนที่ session ที่ใช้หน่วยความจำ?
Session ที่ใช้หน่วยความจำไม่คงอยู่หลังจากรีสตาร์ทเซิร์ฟเวอร์และไม่สามารถแชร์ข้าม instance ได้ Redis ให้ความคงทน, horizontal scaling และคุณสมบัติ expiration ในตัวสำหรับการจัดการ session
คำถาม 4: อธิบายความแตกต่างระหว่าง cache TTL และ sliding expiration?
TTL แบบคงที่จะหมดอายุ entry ในช่วงเวลาที่กำหนดตั้งแต่สร้าง Sliding expiration จะรีเซ็ตตัวจับเวลาทุกครั้งที่มีการเข้าถึงข้อมูล Redis ดั้งเดิมรองรับเฉพาะ TTL แบบคงที่; sliding expiration ต้องการ logic ของแอปพลิเคชันเพื่ออัปเดตเวลา expiration เมื่ออ่าน
คำถาม 5: จะ cache ข้อมูลที่แตกต่างกันต่อ user ได้อย่างไร?
ใช้ cache key ที่รวม user identifier: user:${userId}:preferences สิ่งนี้รับประกันการแยกระหว่างข้อมูลของ user ที่แตกต่างกันในขณะที่ยังได้รับประโยชน์จาก caching
คำถาม 6: การแลกเปลี่ยนระหว่าง cache hit rate และ memory usage คืออะไร?
TTL ที่สูงกว่าเพิ่ม hit rate แต่ใช้หน่วยความจำมากขึ้นและมีความเสี่ยงที่ข้อมูลจะเก่า TTL ที่สั้นกว่ารักษาข้อมูลให้ใหม่แต่เพิ่มภาระ database วิธีแก้ไขที่ดีที่สุดสร้างสมดุลทั้งสองปัจจัยตามรูปแบบการเข้าถึงและความอ่อนไหวของข้อมูล
สรุป
การผสาน Redis กับ NestJS ต้องการความเข้าใจเกี่ยวกับรูปแบบ caching, กลยุทธ์การจัดการ session และการพิจารณาการกำหนดค่า โมดูล @nestjs/cache-manager ให้ abstraction ที่สะอาดสำหรับการดำเนินการ caching ในขณะที่ express-session กับ connect-redis จัดการการเก็บรักษา session การผสมผสานทั้งสองช่วยให้แอปพลิเคชัน NestJS scale ได้ในแนวนอนในขณะที่รักษาประสิทธิภาพและการจัดการสถานะที่สอดคล้องกัน
คุณหาบั๊กใน Node.js / NestJS เจอไหม
โค้ดจริงหนึ่งชิ้น บั๊กที่ซ่อนอยู่หนึ่งจุด วันละหนึ่งครั้ง ลองได้โดยไม่ต้องมีบัญชี

เขียนโดย
Anthony Fillion-Mailletผู้ก่อตั้ง SharpSkill
เป็นนักพัฒนาฟูลสแตกมากว่า 10 ปี ดูแล SharpSkill และรับผิดชอบทุกสิ่งที่เผยแพร่ที่นี่
อัปเดตเมื่อ 10 กันยายน 2569
แท็ก
แชร์
บทความที่เกี่ยวข้อง

NestJS และ MongoDB ในปี 2026: Mongoose, Aggregation และคำถามสัมภาษณ์
เชี่ยวชาญ NestJS กับ MongoDB และ Mongoose 9 เรียนรู้การออกแบบ schema, aggregation pipeline และเตรียมตัวสัมภาษณ์เทคนิคด้วยตัวอย่างจริง

NestJS และ WebSockets ในปี 2026: Real-Time, Gateway และคำถามสัมภาษณ์งาน
คู่มือครบถ้วนสำหรับการสร้างแอปพลิเคชัน real-time ด้วย NestJS และ WebSockets รวมถึงการใช้งาน gateway, best practices และคำถามสัมภาษณ์ทางเทคนิค

Microservices ด้วย NestJS ปี 2026: สถาปัตยกรรม, gRPC และคำถามสัมภาษณ์งาน
คู่มือฉบับสมบูรณ์เรื่องสถาปัตยกรรม NestJS Microservices กับ gRPC: transport layer, Protocol Buffers, streaming patterns และคำถามสัมภาษณ์งานสำหรับ backend engineer ปี 2026