NestJS y Redis en 2026: Caché, Sesiones y Preguntas de Entrevista
Guía completa sobre la integración de Redis con NestJS: configuración del cache-manager, gestión de sesiones distribuidas y preguntas técnicas para entrevistas de desarrollador.

La integración de Redis con NestJS transforma el rendimiento de las aplicaciones al reemplazar las consultas a base de datos con búsquedas en memoria. La combinación de @nestjs/cache-manager para caché y express-session con connect-redis para persistencia de sesiones cubre los dos casos de uso de Redis más comunes en aplicaciones backend.
El caché de Redis en NestJS reduce la carga en la base de datos almacenando datos frecuentemente consultados en memoria. La gestión de sesiones con Redis permite el escalado horizontal al compartir el estado de sesiones entre múltiples instancias.
Configuración de @nestjs/cache-manager con Redis
El módulo cache-manager se trasladó a un paquete separado en NestJS 10. Su instalación requiere tanto el wrapper de NestJS como el store de caché subyacente. El adaptador @keyv/redis es el enfoque recomendado para la integración de Redis con cache-manager v5.
# Instalación de paquetes requeridos
npm install @nestjs/cache-manager cache-manager @keyv/redisEl registro del módulo configura la conexión a Redis y el TTL predeterminado. La opción isGlobal hace que el caché esté disponible en todos los módulos sin necesidad de reimportarlo.
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 predeterminado en milisegundos
}),
}),
],
})
export class AppModule {}El array stores acepta múltiples stores de caché para caching multinivel. Los entornos de producción típicamente usan Redis como store principal, con un fallback opcional en memoria para desarrollo.
Inyección del Caché y Caching a Nivel de Servicio
El token CACHE_MANAGER proporciona acceso directo al caché para operaciones a nivel de servicio. Este enfoque ofrece más control que el caching HTTP automático cuando la lógica de negocio dicta la invalidación del caché.
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> {
// Verificar caché primero
const cacheKey = `user:${id}`;
const cached = await this.cache.get<User>(cacheKey);
if (cached) {
return cached;
}
// Cache miss: obtener de la base de datos
const user = await this.usersRepository.findById(id);
if (user) {
await this.cache.set(cacheKey, user, 300000); // TTL de 5 minutos
}
return user;
}
async update(id: string, data: Partial<User>): Promise<User> {
const user = await this.usersRepository.update(id, data);
// Invalidar caché después de la actualización
await this.cache.del(`user:${id}`);
return user;
}
}El patrón cache-aside implementado arriba verifica el caché antes de consultar la base de datos. Las operaciones de escritura invalidan la entrada del caché para garantizar la consistencia de los datos.
Decorador CacheInterceptor para Caching HTTP
El CacheInterceptor proporciona caching automático de respuestas para endpoints REST. Utiliza la URL de la petición como clave de caché por defecto, lo que lo hace ideal para endpoints GET que sirven datos estáticos o que cambian con poca frecuencia.
import { Controller, Get, 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) // Override TTL: 2 minutos
async findAll() {
return this.productsService.findAll();
}
@Get('featured')
@CacheKey('products:featured')
@CacheTTL(300000) // 5 minutos
async findFeatured() {
return this.productsService.findFeatured();
}
}El decorador @CacheTTL() sobrescribe el TTL predeterminado para rutas específicas. El decorador @CacheKey() permite claves de caché personalizadas en lugar del esquema predeterminado basado en URL.
Gestión de Sesiones con Redis
La gestión de sesiones requiere express-session con el store de Redis. Esta configuración habilita el almacenamiento persistente de sesiones que sobrevive a los reinicios del servidor y funciona con múltiples instancias de la aplicación.
# Instalación de dependencias de sesión
npm install express-session connect-redis ioredis
npm install -D @types/express-sessionLa configuración de sesión se realiza en el archivo main.ts antes de la inicialización de la aplicación NestJS.
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 || 'change-this-secret',
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000, // 24 horas
sameSite: 'lax',
},
}),
);
await app.listen(3000);
}
bootstrap();La opción resave: false evita operaciones de guardado innecesarias. La opción saveUninitialized: false previene la creación de sesiones vacías, lo cual es importante para el cumplimiento del GDPR.
Acceso a Sesiones en Controladores
El objeto de sesión está disponible a través del objeto request de Express. Un decorador personalizado simplifica el acceso a sesiones en los handlers de ruta.
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { Request } from 'express';
export const Session = createParamDecorator(
(data: string | undefined, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest<Request>();
const session = request.session;
return data ? session?.[data] : session;
},
);
// auth.controller.ts
import { Controller, Post, Body, Get } from '@nestjs/common';
import { Session } from './session.decorator';
import { AuthService } from './auth.service';
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
@Post('login')
async login(
@Body() credentials: LoginDto,
@Session() session: Record<string, any>,
) {
const user = await this.authService.validateUser(credentials);
if (user) {
session.userId = user.id;
session.loginAt = new Date().toISOString();
return { success: true };
}
return { success: false };
}
@Post('logout')
async logout(@Session() session: Record<string, any>) {
return new Promise((resolve) => {
session.destroy((err: Error | null) => {
resolve({ success: !err });
});
});
}
@Get('profile')
async getProfile(@Session('userId') userId: string) {
if (!userId) {
return { authenticated: false };
}
return this.authService.getUserProfile(userId);
}
}Patrones Avanzados de Caching
Las aplicaciones de producción frecuentemente requieren estrategias de caching más sofisticadas. El patrón write-through actualiza el caché simultáneamente con la base de datos, mientras que el patrón cache-aside carga datos bajo demanda.
import { Injectable, Inject } from '@nestjs/common';
import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager';
import { OrdersRepository } from './orders.repository';
import { Order } from './order.entity';
@Injectable()
export class OrdersService {
constructor(
@Inject(CACHE_MANAGER) private cache: Cache,
private ordersRepository: OrdersRepository,
) {}
// Patrón Write-Through
async create(orderData: CreateOrderDto): Promise<Order> {
const order = await this.ordersRepository.create(orderData);
// Escribir en caché inmediatamente
await this.cache.set(`order:${order.id}`, order, 600000);
// Invalidar cachés de lista
await this.invalidateListCaches(order.userId);
return order;
}
// Patrón de prefijos de claves de caché
async findByUserId(userId: string): Promise<Order[]> {
const cacheKey = `orders:user:${userId}`;
const cached = await this.cache.get<Order[]>(cacheKey);
if (cached) {
return cached;
}
const orders = await this.ordersRepository.findByUserId(userId);
await this.cache.set(cacheKey, orders, 300000);
return orders;
}
private async invalidateListCaches(userId: string): Promise<void> {
await this.cache.del(`orders:user:${userId}`);
await this.cache.del('orders:recent');
}
}Gestión de Conexión Redis y Health Checks
Las aplicaciones de producción requieren monitoreo de conexiones Redis y endpoints de health check.
import { Injectable } from '@nestjs/common';
import { HealthIndicator, HealthIndicatorResult, HealthCheckError } from '@nestjs/terminus';
import { Redis } from 'ioredis';
@Injectable()
export class RedisHealthIndicator extends HealthIndicator {
constructor(private redis: Redis) {
super();
}
async isHealthy(key: string): Promise<HealthIndicatorResult> {
try {
const start = Date.now();
await this.redis.ping();
const latency = Date.now() - start;
return this.getStatus(key, true, { latency: `${latency}ms` });
} catch (error) {
throw new HealthCheckError(
'Redis health check failed',
this.getStatus(key, false, { error: error.message }),
);
}
}
}¿Listo para aprobar tus entrevistas de Node.js / NestJS?
Practica con nuestros simuladores interactivos, flashcards y tests técnicos.
Preguntas de Entrevista Comunes sobre Caching con Redis
Las entrevistas técnicas evalúan frecuentemente la comprensión de patrones de caching y sus compromisos. Estas preguntas cubren conceptos fundamentales y escenarios reales de producción.
P: ¿Cuál es la diferencia entre los patrones cache-aside y write-through?
El patrón cache-aside carga datos en el caché solo cuando se accede a ellos, lo que reduce el uso de memoria del caché pero puede crear cache misses iniciales. El patrón write-through actualiza el caché durante las operaciones de escritura, garantizando que el caché siempre contenga datos frescos pero aumentando la latencia de escritura.
P: ¿Cómo manejar la invalidación del caché en un sistema distribuido?
Los enfoques incluyen: invalidación basada en eventos usando Redis Pub/Sub, TTL con refrescos eventualmente consistentes, y estrategias de versionado de claves de caché. La elección depende de los requisitos de consistencia y la latencia aceptable.
P: ¿Cuándo usar caching a nivel de sesión vs caching a nivel de aplicación?
El caché a nivel de sesión almacena datos específicos del usuario como preferencias o contenido del carrito. El caché a nivel de aplicación almacena datos compartidos como catálogos de productos o configuraciones. Combinar ambos optimiza el uso de recursos.
P: ¿Cómo implementaría un caché distribuido con invalidación?
import { Injectable, Inject } from '@nestjs/common';
import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager';
import { Redis } from 'ioredis';
@Injectable()
export class DistributedCacheService {
private subscriber: Redis;
constructor(
@Inject(CACHE_MANAGER) private cache: Cache,
private redis: Redis,
) {
this.subscriber = redis.duplicate();
this.setupSubscriber();
}
private setupSubscriber(): void {
this.subscriber.subscribe('cache:invalidate');
this.subscriber.on('message', async (channel, message) => {
if (channel === 'cache:invalidate') {
const { pattern } = JSON.parse(message);
await this.invalidatePattern(pattern);
}
});
}
async invalidateAcrossInstances(pattern: string): Promise<void> {
await this.redis.publish(
'cache:invalidate',
JSON.stringify({ pattern }),
);
}
private async invalidatePattern(pattern: string): Promise<void> {
// La implementación depende del cache store
// Para Redis, usar SCAN con pattern matching
const keys = await this.redis.keys(pattern);
if (keys.length > 0) {
await this.redis.del(...keys);
}
}
}P: ¿Cómo prevenir cache stampedes?
Los cache stampedes ocurren cuando múltiples solicitudes intentan simultáneamente regenerar una entrada de caché expirada. Las soluciones incluyen probabilistic early recomputation, bloqueo con semáforos, o distribución de la expiración con TTLs aleatorios.
Buenas Prácticas de Producción
El despliegue de aplicaciones NestJS basadas en Redis en producción requiere atención especial a la gestión de conexiones y estrategias de failover.
import { Module, Global } from '@nestjs/common';
import { Redis } from 'ioredis';
@Global()
@Module({
providers: [
{
provide: 'REDIS_CLIENT',
useFactory: () => {
const redis = new Redis(process.env.REDIS_URL, {
maxRetriesPerRequest: 3,
retryDelayOnFailover: 100,
retryDelayOnClusterDown: 100,
enableReadyCheck: true,
lazyConnect: true,
});
redis.on('error', (err) => {
console.error('Redis connection error:', err);
});
redis.on('connect', () => {
console.log('Redis connected');
});
return redis;
},
},
],
exports: ['REDIS_CLIENT'],
})
export class RedisModule {}El parámetro maxRetriesPerRequest evita que las solicitudes se bloqueen indefinidamente durante fallos de Redis. La opción lazyConnect retrasa la conexión hasta la primera operación, mejorando los tiempos de inicio.
Conclusión
La integración de Redis con NestJS ofrece una solución robusta para caching y gestión de sesiones. La combinación del cache-manager para caché de aplicación y connect-redis para sesiones cubre la mayoría de los casos de uso en producción. Dominar estos patrones es esencial para cualquier desarrollador NestJS que trabaje en aplicaciones de alto tráfico.
¿Sabrías detectar el bug en Node.js / NestJS?
Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Escrito por
Anthony Fillion-MailletFundador de SharpSkill
Desarrollador fullstack desde hace más de 10 años. Dirige SharpSkill y responde por todo lo que se publica aquí.
Actualizado el 10 de septiembre de 2026
Etiquetas
Compartir
Artículos relacionados

ListView JavaScript de Alto Rendimiento en 2026: Virtualización y Técnicas de Optimización
Guía completa sobre la implementación de listas virtualizadas de alto rendimiento en JavaScript. Técnicas de virtualización, optimización del renderizado y mejores prácticas para manejar miles de elementos sin pérdida de rendimiento.

NestJS y MongoDB en 2026: Mongoose, Agregaciones y Preguntas de Entrevista
Guía completa sobre la integración de NestJS 12 con MongoDB mediante Mongoose 9. Aprende sobre esquemas, pipelines de agregación y preguntas técnicas frecuentes en entrevistas.

Entrevista NestJS: Guards, Interceptors y arquitectura modular
Preguntas frecuentes en entrevistas técnicas de NestJS sobre Guards, Interceptors y arquitectura modular, con ejemplos concretos en TypeScript y explicaciones técnicas.