NestJS et Redis en 2026 : Mise en Cache, Sessions et Questions d'Entretien
Guide complet sur l'intégration de Redis avec NestJS : configuration du cache-manager, gestion des sessions distribuées et questions techniques pour les entretiens développeur.

L'intégration de Redis avec NestJS transforme les performances applicatives en remplaçant les appels base de données par des recherches en mémoire. La combinaison de @nestjs/cache-manager pour le cache et express-session avec connect-redis pour la persistance des sessions couvre les deux cas d'utilisation Redis les plus courants dans les applications backend.
Le cache Redis dans NestJS réduit la charge sur la base de données en stockant les données fréquemment consultées en mémoire. La gestion des sessions avec Redis permet le scaling horizontal en partageant l'état des sessions entre plusieurs instances.
Configuration de @nestjs/cache-manager avec Redis
Le module cache-manager a été déplacé dans un package séparé depuis NestJS 10. Son installation nécessite à la fois le wrapper NestJS et le store de cache sous-jacent. L'adaptateur @keyv/redis est l'approche recommandée pour l'intégration Redis avec cache-manager v5.
# Installation des packages requis
npm install @nestjs/cache-manager cache-manager @keyv/redisL'enregistrement du module configure la connexion Redis et le TTL par défaut. L'option isGlobal rend le cache disponible dans tous les modules sans réimportation.
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 par défaut en millisecondes
}),
}),
],
})
export class AppModule {}Le tableau stores accepte plusieurs stores de cache pour un caching multi-niveaux. Les environnements de production utilisent généralement Redis comme store principal, avec un fallback optionnel en mémoire pour le développement.
Injection du Cache et Caching au Niveau Service
Le token CACHE_MANAGER fournit un accès direct au cache pour les opérations au niveau service. Cette approche offre plus de contrôle que le caching HTTP automatique lorsque la logique métier dicte l'invalidation du 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> {
// Vérifier le cache d'abord
const cacheKey = `user:${id}`;
const cached = await this.cache.get<User>(cacheKey);
if (cached) {
return cached;
}
// Cache miss : récupérer depuis la base de données
const user = await this.usersRepository.findById(id);
if (user) {
await this.cache.set(cacheKey, user, 300000); // TTL de 5 minutes
}
return user;
}
async update(id: string, data: Partial<User>): Promise<User> {
const user = await this.usersRepository.update(id, data);
// Invalider le cache après mise à jour
await this.cache.del(`user:${id}`);
return user;
}
}Le pattern cache-aside implémenté ci-dessus vérifie le cache avant d'interroger la base de données. Les opérations d'écriture invalident l'entrée du cache pour garantir la cohérence des données.
Décorateur CacheInterceptor pour le Caching HTTP
Le CacheInterceptor fournit un caching automatique des réponses pour les endpoints REST. Il utilise l'URL de la requête comme clé de cache par défaut, ce qui le rend idéal pour les endpoints GET qui servent des données statiques ou peu modifiées.
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) // Surcharge TTL : 2 minutes
async findAll() {
return this.productsService.findAll();
}
@Get('featured')
@CacheKey('products:featured')
@CacheTTL(300000) // 5 minutes
async findFeatured() {
return this.productsService.findFeatured();
}
}Le décorateur @CacheTTL() surcharge le TTL par défaut pour des routes spécifiques. Le décorateur @CacheKey() permet des clés de cache personnalisées au lieu du schéma par défaut basé sur l'URL.
Gestion des Sessions avec Redis
La gestion des sessions nécessite express-session avec le store Redis. Cette configuration active le stockage de sessions persistant qui survit aux redémarrages du serveur et fonctionne avec plusieurs instances applicatives.
# Installation des dépendances de session
npm install express-session connect-redis ioredis
npm install -D @types/express-sessionLa configuration de session se fait dans le fichier main.ts avant l'initialisation de l'application 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 heures
sameSite: 'lax',
},
}),
);
await app.listen(3000);
}
bootstrap();L'option resave: false évite les opérations de sauvegarde inutiles. L'option saveUninitialized: false empêche la création de sessions vides, ce qui est important pour la conformité RGPD.
Accès aux Sessions dans les Contrôleurs
L'objet session devient disponible via l'objet request Express. Un décorateur personnalisé simplifie l'accès aux sessions dans les handlers de route.
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);
}
}Patterns Avancés de Caching
Les applications de production nécessitent souvent des stratégies de caching plus sophistiquées. Le pattern write-through met à jour le cache simultanément avec la base de données, tandis que le pattern cache-aside charge les données à la demande.
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,
) {}
// Pattern Write-Through
async create(orderData: CreateOrderDto): Promise<Order> {
const order = await this.ordersRepository.create(orderData);
// Écrire dans le cache immédiatement
await this.cache.set(`order:${order.id}`, order, 600000);
// Invalider le cache de liste
await this.invalidateListCaches(order.userId);
return order;
}
// Pattern de préfixes de clés de cache
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');
}
}Gestion de la Connexion Redis et Health Checks
Les applications de production nécessitent une surveillance des connexions Redis et des 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 }),
);
}
}
}Prêt à réussir tes entretiens Node.js / NestJS ?
Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.
Questions d'Entretien Courantes sur le Caching Redis
Les entretiens techniques évaluent fréquemment la compréhension des patterns de caching et de leurs compromis. Ces questions couvrent les concepts fondamentaux et les scénarios réels de production.
Q: Quelle est la différence entre les patterns cache-aside et write-through ?
Le pattern cache-aside charge les données dans le cache uniquement lors de l'accès, ce qui réduit l'utilisation mémoire du cache mais peut créer des cache miss initiaux. Le pattern write-through met à jour le cache pendant les opérations d'écriture, garantissant que le cache contient toujours des données fraîches mais augmentant la latence d'écriture.
Q: Comment gérer l'invalidation du cache dans un système distribué ?
Les approches incluent : l'invalidation basée sur les événements utilisant Redis Pub/Sub, le TTL avec des rafraîchissements éventuellement cohérents, et les stratégies de versioning de clés de cache. Le choix dépend des exigences de cohérence et de la latence acceptable.
Q: Quand utiliser le caching au niveau session vs le caching au niveau application ?
Le cache au niveau session stocke des données spécifiques à l'utilisateur comme les préférences ou le contenu du panier. Le cache au niveau application stocke des données partagées comme les catalogues produits ou les configurations. Mélanger les deux optimise l'utilisation des ressources.
Q: Comment implémenteriez-vous un cache distribué avec invalidation ?
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> {
// L'implémentation dépend du cache store
// Pour Redis, utiliser SCAN avec pattern matching
const keys = await this.redis.keys(pattern);
if (keys.length > 0) {
await this.redis.del(...keys);
}
}
}Q: Comment prévenir les cache stampedes ?
Les cache stampedes surviennent lorsque plusieurs requêtes tentent simultanément de régénérer une entrée de cache expirée. Les solutions incluent le probabilistic early recomputation, le verrouillage avec sémaphores, ou la distribution de l'expiration avec des TTL aléatoires.
Bonnes Pratiques de Production
Le déploiement d'applications NestJS basées sur Redis en production nécessite une attention particulière à la gestion des connexions et aux stratégies 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 {}Le paramètre maxRetriesPerRequest empêche les requêtes de se bloquer indéfiniment pendant les pannes Redis. L'option lazyConnect retarde la connexion jusqu'à la première opération, améliorant les temps de démarrage.
Conclusion
L'intégration Redis avec NestJS offre une solution robuste pour le caching et la gestion des sessions. La combinaison du cache-manager pour le cache applicatif et de connect-redis pour les sessions couvre la majorité des cas d'utilisation en production. La maîtrise de ces patterns est essentielle pour tout développeur NestJS travaillant sur des applications à forte charge.
Tu saurais repérer le bug en Node.js / NestJS ?
Un vrai bout de code, un bug caché, une tentative par jour. Sans compte pour essayer.

Écrit par
Anthony Fillion-MailletFondateur de SharpSkill
Développeur fullstack depuis plus de 10 ans. Il dirige SharpSkill et répond de tout ce qui y est publié.
Mis à jour le 10 septembre 2026
Tags
Partager
Articles similaires

ListView JavaScript Haute Performance en 2026 : Virtualisation et Techniques d'Optimisation
Guide complet sur l'implémentation de listes virtualisées haute performance en JavaScript. Découvrez les techniques de virtualisation, l'optimisation du rendu et les meilleures pratiques pour gérer des milliers d'éléments sans ralentissement.

NestJS et MongoDB en 2026 : Mongoose, Agrégations et Questions d'Entretien
Guide complet sur l'intégration de NestJS 12 avec MongoDB via Mongoose 9. Découvrez les schémas, les pipelines d'agrégation et les questions techniques fréquentes en entretien.

NestJS en entretien : Guards, Interceptors et Architecture modulaire
Les questions d'entretien NestJS sur les Guards, Interceptors et l'architecture modulaire, avec des exemples de code concrets et des explications techniques.