NestJS en Redis in 2026: Caching, Sessies en Technische Sollicitatievragen

NestJS Redis-integratie met @nestjs/cache-manager, sessiebeheer met connect-redis en veelvoorkomende sollicitatievragen voor Node.js backend-ontwikkelaars.

NestJS en Redis in 2026: Caching, Sessies en Technische Sollicitatievragen

De integratie van Redis in NestJS-applicaties transformeert de prestaties door database-aanroepen te vervangen door in-memory lookups. De combinatie van @nestjs/cache-manager voor caching en express-session met connect-redis voor sessiepersistentie dekt de twee meest voorkomende Redis-toepassingen in backend-applicaties.

Kernconcept

Redis-caching in NestJS vermindert de databasebelasting door veelgebruikte gegevens in het geheugen op te slaan. Sessiebeheer met Redis maakt horizontale schaalbaarheid mogelijk door de sessiestatus te delen over meerdere instanties.

Configuratie van @nestjs/cache-manager met Redis

De cache-manager-module is verplaatst naar een apart pakket in NestJS 10. De installatie vereist zowel de NestJS-wrapper als de onderliggende cache-store. De @keyv/redis-adapter is de aanbevolen aanpak voor Redis-integratie met cache-manager v5.

bash
# Vereiste pakketten installeren
npm install @nestjs/cache-manager cache-manager @keyv/redis

De moduleregistratie configureert de Redis-verbinding en de standaard TTL. De isGlobal-optie maakt de cache beschikbaar in alle modules zonder opnieuw te importeren.

app.module.tstypescript
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, // Standaard TTL in milliseconden
      }),
    }),
  ],
})
export class AppModule {}

De stores-array accepteert meerdere cache-stores voor multi-tier caching. Productieomgevingen gebruiken typisch Redis als primaire store, met een optionele in-memory fallback voor ontwikkeling.

Cache-injectie en Service-Level Caching

Het CACHE_MANAGER-token biedt directe toegang tot de cache voor service-level operaties. Deze aanpak biedt meer controle dan automatische HTTP-caching wanneer de bedrijfslogica de cache-invalidatie bepaalt.

users.service.tstypescript
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> {
    // Controleer eerst de cache
    const cacheKey = `user:${id}`;
    const cached = await this.cache.get<User>(cacheKey);
    
    if (cached) {
      return cached;
    }

    // Cache miss: ophalen uit database
    const user = await this.usersRepository.findById(id);
    
    if (user) {
      await this.cache.set(cacheKey, user, 300000); // 5 minuten TTL
    }

    return user;
  }

  async update(id: string, data: Partial<User>): Promise<User> {
    const user = await this.usersRepository.update(id, data);
    // Cache invalideren bij update
    await this.cache.del(`user:${id}`);
    
    return user;
  }
}

Het cache-sleutelpatroon user:{id} maakt gerichte invalidatie mogelijk. Complexere patronen zoals user:* vereisen Redis-specifieke commando's via de onderliggende client.

CacheInterceptor voor Automatische HTTP Response Caching

De ingebouwde CacheInterceptor cached HTTP GET-responses automatisch. Toepassing op controller-niveau cached alle GET-endpoints, terwijl toepassing op methode-niveau granulaire controle biedt.

products.controller.tstypescript
import { Controller, Get, UseInterceptors, Param } 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) // Standaard TTL overschrijven: 2 minuten
  async findAll() {
    return this.productsService.findAll();
  }

  @Get(':id')
  @CacheKey('product-detail') // Aangepaste cache-sleutel prefix
  async findOne(@Param('id') id: string) {
    return this.productsService.findById(id);
  }
}

De interceptor genereert standaard cache-sleutels uit de request-URL. Aangepaste @CacheKey()-decorators overschrijven dit gedrag voor routes waar query-parameters geen invloed moeten hebben op caching. NestJS-interceptors en guards zijn frequente onderwerpen in technische sollicitatiegesprekken.

Cache-sleutel Botsingen

De standaard cache-sleutel gebruikt de volledige URL inclusief query-parameters. Twee verzoeken naar /products?page=1 en /products?page=2 creëren afzonderlijke cache-entries. Overschrijf met @CacheKey() wanneer paginering geen dubbele gecachte responses moet creëren.

Sessiebeheer met Redis en express-session

NestJS draait standaard op Express, waardoor express-session de standaardkeuze is voor sessiebeheer. De connect-redis-adapter slaat sessiegegevens op in Redis in plaats van in het geheugen, waardoor sessiepersistentie over server-herstarts mogelijk wordt.

bash
# Sessiepakketten installeren
npm install express-session connect-redis redis
npm install -D @types/express-session

De sessieconfiguratie gebeurt in main.ts voordat de applicatie start. De Redis-client verbindt onafhankelijk van de cache-manager setup.

main.tstypescript
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as session from 'express-session';
import { createClient } from 'redis';
import RedisStore from 'connect-redis';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Redis-client initialiseren
  const redisClient = createClient({
    url: process.env.REDIS_URL || 'redis://localhost:6379',
  });
  await redisClient.connect();

  // Sessie-middleware configureren
  app.use(
    session({
      store: new RedisStore({ client: redisClient }),
      secret: process.env.SESSION_SECRET || 'change-this-in-production',
      resave: false,
      saveUninitialized: false,
      cookie: {
        httpOnly: true,
        secure: process.env.NODE_ENV === 'production',
        maxAge: 24 * 60 * 60 * 1000, // 24 uur
      },
    }),
  );

  await app.listen(3000);
}
bootstrap();

De saveUninitialized: false-optie voorkomt het opslaan van lege sessies, waardoor Redis-geheugengebruik wordt verminderd. De secure: true-cookie-instelling vereist HTTPS in productie.

Klaar om je Node.js / NestJS gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

Toegang tot Sessies in Controllers en Guards

Sessiegegevens zijn beschikbaar via het request-object. Een getypeerde sessie-interface verbetert de ontwikkelaarservaring en detecteert fouten tijdens compilatie.

session.interface.tstypescript
export interface SessionData {
  userId?: string;
  email?: string;
  roles?: string[];
  loginAt?: Date;
}

declare module 'express-session' {
  interface SessionData {
    userId?: string;
    email?: string;
    roles?: string[];
    loginAt?: Date;
  }
}

Het uitgebreide sessietype maakt sessie-eigenschappen beschikbaar met autocompletion.

auth.controller.tstypescript
import { Controller, Post, Body, Req, HttpCode } from '@nestjs/common';
import { Request } from 'express';
import { AuthService } from './auth.service';
import { LoginDto } from './dto/login.dto';

@Controller('auth')
export class AuthController {
  constructor(private authService: AuthService) {}

  @Post('login')
  @HttpCode(200)
  async login(@Body() loginDto: LoginDto, @Req() req: Request) {
    const user = await this.authService.validateUser(
      loginDto.email,
      loginDto.password,
    );

    // Gebruikersgegevens opslaan in sessie
    req.session.userId = user.id;
    req.session.email = user.email;
    req.session.roles = user.roles;
    req.session.loginAt = new Date();

    return { message: 'Succesvol ingelogd' };
  }

  @Post('logout')
  @HttpCode(200)
  async logout(@Req() req: Request) {
    return new Promise((resolve, reject) => {
      req.session.destroy((err) => {
        if (err) reject(err);
        resolve({ message: 'Succesvol uitgelogd' });
      });
    });
  }
}

Een guard beschermt routes door de geldigheid van de sessie te controleren. Dit patroon integreert naadloos met het NestJS-autorisatie- en RBAC-systeem.

session.guard.tstypescript
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Request } from 'express';

@Injectable()
export class SessionGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest<Request>();
    return !!request.session?.userId;
  }
}

Cache-invalidatiestrategieën voor NestJS-applicaties

Cache-invalidatie is het moeilijkere probleem bij caching. Drie strategieën zijn geschikt voor verschillende scenario's:

Tijdgebaseerde vervaldatum (TTL) werkt voor gegevens die voorspelbaar veranderen of waar lichte veroudering acceptabel is. Productcatalogi, configuratiewaarden en openbare content passen in dit patroon.

Gebeurtenisgebaseerde invalidatie wist cache-entries wanneer de onderliggende gegevens veranderen. Dit vereist expliciete cache.del()-aanroepen in update- en delete-operaties.

Patroongebaseerde invalidatie verwijdert meerdere gerelateerde sleutels. Redis ondersteunt patroonverwijdering via SCAN- en DEL-commando's, maar cache-manager abstraheert dit.

cache-invalidation.service.tstypescript
import { Injectable, Inject } from '@nestjs/common';
import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager';

@Injectable()
export class CacheInvalidationService {
  constructor(@Inject(CACHE_MANAGER) private cache: Cache) {}

  // Enkele sleutel-invalidatie
  async invalidateUser(userId: string): Promise<void> {
    await this.cache.del(`user:${userId}`);
  }

  // Meerdere gerelateerde sleutels
  async invalidateUserRelated(userId: string): Promise<void> {
    const keys = [
      `user:${userId}`,
      `user:${userId}:profile`,
      `user:${userId}:preferences`,
    ];
    
    await Promise.all(keys.map((key) => this.cache.del(key)));
  }

  // Hele cache wissen (spaarzaam gebruiken)
  async clearAll(): Promise<void> {
    await this.cache.reset();
  }
}

De reset()-methode wist alle gecachte gegevens, nuttig tijdens deployments of datamigraties maar gevaarlijk in productie als deze per ongeluk wordt aangeroepen.

Redis Connection Pooling en Cluster-ondersteuning

Productie-deployments vereisen connection pooling om gelijktijdige verzoeken efficiënt af te handelen. De ioredis-bibliotheek biedt standaard cluster-ondersteuning en connection pooling.

redis.config.tstypescript
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import Redis, { Cluster } from 'ioredis';

@Injectable()
export class RedisConfig {
  constructor(private configService: ConfigService) {}

  createClient(): Redis | Cluster {
    const clusterNodes = this.configService.get<string>('REDIS_CLUSTER_NODES');

    if (clusterNodes) {
      // Cluster-modus
      const nodes = clusterNodes.split(',').map((node) => {
        const [host, port] = node.split(':');
        return { host, port: parseInt(port, 10) };
      });

      return new Redis.Cluster(nodes, {
        redisOptions: {
          password: this.configService.get('REDIS_PASSWORD'),
        },
      });
    }

    // Standalone-modus
    return new Redis({
      host: this.configService.get('REDIS_HOST', 'localhost'),
      port: this.configService.get('REDIS_PORT', 6379),
      password: this.configService.get('REDIS_PASSWORD'),
      maxRetriesPerRequest: 3,
    });
  }
}

Voor applicaties die TypeORM of Prisma gebruiken, moeten database- en cache-verbindingen afzonderlijk worden geconfigureerd met hun eigen connection pools.

NestJS Redis Sollicitatievragen

Technische sollicitatiegesprekken over Node.js-caching behandelen zowel conceptueel begrip als praktische implementatie. Deze vragen komen vaak voor:

Waarom Redis gebruiken in plaats van in-memory caching?

In-memory caching persisteert niet over herstarts en kan niet worden gedeeld over meerdere instanties. Redis biedt persistentie, replicatie en gedeelde status voor horizontaal geschaalde applicaties. De afweging is netwerklatentie vergeleken met lokale geheugentoegang.

Hoe verschilt cache-manager van direct Redis-gebruik?

De cache-manager-bibliotheek biedt een uniforme API over verschillende cache-stores. Overschakelen van in-memory naar Redis vereist alleen het wijzigen van de store-configuratie zonder de servicecode aan te passen. Direct Redis-gebruik biedt meer Redis-specifieke functies zoals pub/sub, sorted sets en Lua-scripting.

Wat is cache stampede en hoe voorkom je het?

Cache stampede treedt op wanneer een populaire cache-entry verloopt en veel gelijktijdige verzoeken tegelijkertijd de database raken. Preventiestrategieën omvatten:

  • Lock-gebaseerde refresh: Eén verzoek vernieuwt de cache terwijl anderen wachten
  • Probabilistische vroege vervaldatum: Willekeurige verversing voor TTL-vervaldatum
  • Achtergrondverversing: Een apart proces vernieuwt verlopende entries

Wanneer CacheInterceptor vs. service-level caching gebruiken?

CacheInterceptor is geschikt voor stateless GET-endpoints waar de response alleen van de URL afhangt. Service-level caching behandelt scenario's waar cache-invalidatie moet plaatsvinden bij datawijzigingen of waar niet-GET-operaties caching nodig hebben.

Hoe voorkom je session fixation-aanvallen met Redis-sessies?

Regenereer de sessie-ID na authenticatie. Roep req.session.regenerate() aan voordat gebruikersreferenties worden opgeslagen om te voorkomen dat aanvallers pre-authenticatie sessie-ID's gebruiken.

typescript
// Veilige login met sessie-regeneratie
async login(@Body() loginDto: LoginDto, @Req() req: Request) {
  const user = await this.authService.validateUser(
    loginDto.email,
    loginDto.password,
  );

  return new Promise((resolve, reject) => {
    req.session.regenerate((err) => {
      if (err) reject(err);
      
      req.session.userId = user.id;
      req.session.email = user.email;
      resolve({ message: 'Succesvol ingelogd' });
    });
  });
}
Sollicitatie-inzicht

Senior kandidaten leggen de afwegingen tussen cachingstrategieën uit. Een junior zou kunnen zeggen "caching verbetert de prestaties." Een senior legt uit wanneer caching complexiteit toevoegt zonder voordeel, zoals bij sterk gepersonaliseerde of snel veranderende gegevens.

Prestatiemonitoring en Redis-metrics

Productieapplicaties hebben inzicht nodig in cacheprestaties. Belangrijke metrics omvatten:

  • Hit rate: Percentage verzoeken dat uit de cache wordt bediend. Onder 80% suggereert TTL- of sleutelstrategieproblemen.
  • Geheugengebruik: Het Redis INFO memory-commando toont huidig en piekgeheugen.
  • Verbindingsaantal: Te veel verbindingen duiden op ontbrekende pooling.
  • Eviction-aantal: Niet-nul evictions betekenen dat de cache vol is en entries verwijdert.
redis-health.service.tstypescript
import { Injectable } from '@nestjs/common';
import Redis from 'ioredis';

@Injectable()
export class RedisHealthService {
  constructor(private redis: Redis) {}

  async getMetrics() {
    const info = await this.redis.info('stats');
    const memory = await this.redis.info('memory');
    
    const stats = this.parseInfo(info);
    const mem = this.parseInfo(memory);

    const hits = parseInt(stats.keyspace_hits, 10);
    const misses = parseInt(stats.keyspace_misses, 10);
    const hitRate = hits / (hits + misses) || 0;

    return {
      hitRate: (hitRate * 100).toFixed(2) + '%',
      usedMemory: mem.used_memory_human,
      connectedClients: stats.connected_clients,
      evictedKeys: stats.evicted_keys,
    };
  }

  private parseInfo(info: string): Record<string, string> {
    return info.split('\n').reduce((acc, line) => {
      const [key, value] = line.split(':');
      if (key && value) acc[key.trim()] = value.trim();
      return acc;
    }, {} as Record<string, string>);
  }
}

Exposeer deze metrics via een health check-endpoint of integreer met Prometheus voor productiemonitoring.

Redis Caching en Sessies in Productie NestJS-deployments

Het deployen van NestJS met Redis vereist aandacht voor verschillende operationele aspecten:

  • Connection string-beheer: Sla Redis-URL's op in omgevingsvariabelen, niet in code. Gebruik secrets-management voor wachtwoorden.
  • Graceful shutdown: Sluit Redis-verbindingen tijdens applicatie-shutdown om connection leaks te voorkomen.
  • Retry-logica: Configureer exponentiële backoff voor Redis-verbindingsfouten.
  • Afzonderlijke Redis-instanties: Overweeg afzonderlijke Redis-instanties voor cache en sessies. Cache-eviction mag sessiegegevens niet beïnvloeden.
graceful-shutdown.tstypescript
import { Injectable, OnModuleDestroy } from '@nestjs/common';
import Redis from 'ioredis';

@Injectable()
export class RedisCleanup implements OnModuleDestroy {
  constructor(private redis: Redis) {}

  async onModuleDestroy() {
    await this.redis.quit();
  }
}

Voor microservices-architecturen dient Redis ook als message broker via de ingebouwde transportlaag van NestJS, waardoor caching, sessies en inter-service communicatie worden verenigd.

Begin met oefenen!

Test je kennis met onze gespreksimulatoren en technische tests.

Dagelijkse challenge

Zie jij de bug in Node.js / NestJS?

Een echt codefragment, een verborgen bug, één poging per dag. Zonder account uit te proberen.

Anthony Fillion-Maillet

Geschreven door

Anthony Fillion-Maillet

Oprichter van SharpSkill

Al meer dan 10 jaar fullstack-ontwikkelaar. Hij leidt SharpSkill en staat in voor alles wat hier verschijnt.

Bijgewerkt op 10 september 2026

Delen

Gerelateerde artikelen