2026'da NestJS ve Redis: Önbellekleme, Oturumlar ve Mülakat Soruları

NestJS'te Redis entegrasyonu için kapsamlı rehber. Cache-manager, oturum yönetimi ve invalidasyon stratejileri.

2026'da NestJS ve Redis: Önbellekleme, Oturumlar ve Mülakat Soruları

NestJS'e Redis entegrasyonu, veritabanı sorgularını bellek içi aramalarla değiştirerek uygulama performansını dönüştürür. Önbellekleme için @nestjs/cache-manager ve oturum kalıcılığı için connect-redis ile express-session kombinasyonu, backend uygulamalarındaki en yaygın iki Redis kullanım senaryosunu kapsar.

Ana Fikir

NestJS'te Redis önbellekleme, sık erişilen verileri bellekte saklayarak veritabanı yükünü azaltır. Redis ile oturum yönetimi, oturum durumunu birden fazla örnek arasında paylaşarak yatay ölçeklendirmeyi mümkün kılar.

@nestjs/cache-manager'ı Redis ile Yapılandırma

Cache-manager modülü NestJS 10'da ayrı bir pakete taşındı. Kurulum hem NestJS wrapper'ını hem de temel cache store'u gerektirir. @keyv/redis adaptörü, cache-manager v5 ile Redis entegrasyonu için önerilen yaklaşımdır.

bash
# Install required packages
npm install @nestjs/cache-manager cache-manager @keyv/redis

Modül kaydı, Redis bağlantısını ve varsayılan TTL'i yapılandırır. isGlobal seçeneği, önbelleği yeniden içe aktarmaya gerek kalmadan tüm modüllerde kullanılabilir hale getirir.

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, // Default TTL in milliseconds
      }),
    }),
  ],
})
export class AppModule {}

stores dizisi, çok katmanlı önbellekleme için birden fazla cache store kabul eder. Üretim ortamları genellikle Redis'i birincil store olarak kullanır ve geliştirme için isteğe bağlı bellek içi fallback sağlar.

Cache Enjeksiyonu ve Servis Düzeyinde Önbellekleme

CACHE_MANAGER tokeni, servis düzeyindeki işlemler için önbelleğe doğrudan erişim sağlar. Bu yaklaşım, iş mantığı önbellek invalidasyonunu belirlediğinde otomatik HTTP önbelleklemesinden daha fazla kontrol sunar.

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> {
    // Check cache first
    const cacheKey = `user:${id}`;
    const cached = await this.cache.get<User>(cacheKey);
    
    if (cached) {
      return cached;
    }

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

    return user;
  }

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

user:{id} önbellek anahtarı deseni, hedefli invalidasyonu mümkün kılar. user:* gibi daha karmaşık desenler, temel istemci aracılığıyla Redis'e özgü komutlar gerektirir.

Otomatik HTTP Yanıt Önbellekleme için CacheInterceptor

Yerleşik CacheInterceptor, HTTP GET yanıtlarını otomatik olarak önbelleğe alır. Controller düzeyinde uygulamak tüm GET endpoint'lerini önbelleğe alırken, method düzeyinde uygulama granüler kontrol sağlar.

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) // Override default TTL: 2 minutes
  async findAll() {
    return this.productsService.findAll();
  }

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

Interceptor varsayılan olarak istek URL'sinden önbellek anahtarları üretir. Özel @CacheKey() dekoratörleri, sorgu parametrelerinin önbelleklemeyi etkilememesi gereken rotalar için bu davranışı geçersiz kılar. NestJS'teki interceptor ve guard kavramları teknik mülakatlarda sıkça karşılaşılır.

Önbellek Anahtarı Çakışmaları

Varsayılan önbellek anahtarı, sorgu parametreleri dahil tam URL'yi kullanır. /products?page=1 ve /products?page=2 için iki istek ayrı önbellek girişleri oluşturur. Sayfalama yinelenen önbellek yanıtları oluşturmaması gerektiğinde @CacheKey() ile geçersiz kılın.

Redis ve express-session ile Oturum Yönetimi

NestJS varsayılan olarak Express üzerinde çalışır, bu da express-session'ı oturum yönetimi için standart seçim yapar. connect-redis adaptörü oturum verilerini bellek yerine Redis'te depolar ve sunucu yeniden başlatmalarında oturum kalıcılığını sağlar.

bash
# Install session packages
npm install express-session connect-redis redis
npm install -D @types/express-session

Oturum yapılandırması uygulama başlamadan önce main.ts'de gerçekleşir. Redis istemcisi cache-manager kurulumundan bağımsız olarak bağlanır.

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);

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

  // Configure session middleware
  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 hours
      },
    }),
  );

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

saveUninitialized: false seçeneği boş oturumların kaydedilmesini önleyerek Redis bellek kullanımını azaltır. secure: true çerez ayarı üretimde HTTPS gerektirir.

Node.js / NestJS mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Controller ve Guard'larda Oturumlara Erişim

Oturum verileri request nesnesi aracılığıyla erişilebilir. Tipli bir oturum arayüzü geliştirici deneyimini iyileştirir ve derleme zamanında hataları yakalar.

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;
  }
}

Genişletilmiş oturum tipi, oturum özelliklerini otomatik tamamlama ile kullanılabilir hale getirir.

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,
    );

    // Store user data in session
    req.session.userId = user.id;
    req.session.email = user.email;
    req.session.roles = user.roles;
    req.session.loginAt = new Date();

    return { message: 'Logged in successfully' };
  }

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

Bir guard, oturum geçerliliğini kontrol ederek rotaları korur. Bu desen, NestJS'in yetkilendirme ve RBAC sistemiyle sorunsuz entegre olur.

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;
  }
}

NestJS Uygulamalarında Önbellek Invalidasyon Stratejileri

Önbellek invalidasyonu, önbelleklemedeki daha zor problemdir. Üç strateji farklı senaryolara uygulanır:

Zamana dayalı sona erme (TTL) tahmin edilebilir şekilde değişen veya hafif eskiliğin kabul edilebilir olduğu veriler için çalışır. Ürün katalogları, yapılandırma değerleri ve genel içerik bu desene uyar.

Olay tabanlı invalidasyon temel veriler değiştiğinde önbellek girişlerini temizler. Bu, güncelleme ve silme işlemlerinde açık cache.del() çağrıları gerektirir.

Desen tabanlı invalidasyon birden fazla ilişkili anahtarı kaldırır. Redis, SCAN ve DEL komutları aracılığıyla desen silmeyi destekler, ancak cache-manager bunu soyutlar.

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) {}

  // Single key invalidation
  async invalidateUser(userId: string): Promise<void> {
    await this.cache.del(`user:${userId}`);
  }

  // Multiple related keys
  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)));
  }

  // Clear entire cache (use sparingly)
  async clearAll(): Promise<void> {
    await this.cache.reset();
  }
}

reset() metodu tüm önbelleğe alınmış verileri temizler, dağıtımlar veya veri geçişleri sırasında yararlıdır ancak yanlışlıkla çağrılırsa üretimde tehlikelidir.

Redis Bağlantı Havuzlama ve Küme Desteği

Üretim dağıtımları, eşzamanlı istekleri verimli bir şekilde işlemek için bağlantı havuzlaması gerektirir. ioredis kütüphanesi, küme desteği ve bağlantı havuzlamasını hazır olarak sağlar.

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 mode
      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 mode
    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,
    });
  }
}

TypeORM veya Prisma kullanan uygulamalar için veritabanı ve önbellek bağlantıları kendi bağlantı havuzlarıyla ayrı olarak yapılandırılmalıdır.

NestJS Redis Mülakat Soruları

Node.js önbellekleme üzerine teknik mülakatlar hem kavramsal anlayışı hem de pratik uygulamayı kapsar. Bu sorular sıkça karşılaşılır:

Bellek içi önbellekleme yerine neden Redis kullanılır?

Bellek içi önbellekleme yeniden başlatmalarda kalıcı değildir ve birden fazla örnek arasında paylaşılamaz. Redis, yatay olarak ölçeklenen uygulamalar için kalıcılık, replikasyon ve paylaşılan durum sağlar. Değiş tokuş, yerel bellek erişimine kıyasla ağ gecikmesidir.

cache-manager doğrudan Redis kullanımından nasıl farklıdır?

cache-manager kütüphanesi, farklı cache store'lar arasında birleşik bir API sağlar. Bellekten Redis'e geçiş, servis kodunu değiştirmeden store yapılandırmasının değiştirilmesini gerektirir. Doğrudan Redis kullanımı, pub/sub, sorted sets ve Lua scripting gibi daha fazla Redis'e özgü özellik sunar.

Cache stampede nedir ve nasıl önlenir?

Cache stampede, popüler bir önbellek girişi sona erdiğinde ve birçok eşzamanlı istek aynı anda veritabanına gittiğinde oluşur. Önleme stratejileri şunları içerir:

  • Kilitleme tabanlı yenileme: Bir istek önbelleği yenilerken diğerleri bekler
  • Olasılıksal erken sona erme: TTL sona ermeden önce rastgele yenileme
  • Arka plan yenileme: Ayrı bir işlem sona eren girişleri yeniler

CacheInterceptor vs. servis düzeyinde önbellekleme ne zaman kullanılmalı?

CacheInterceptor, yanıtın yalnızca URL'ye bağlı olduğu durumsuz GET endpoint'leri için uygundur. Servis düzeyinde önbellekleme, önbellek invalidasyonunun veri değişikliklerinde gerçekleşmesi gerektiği veya GET olmayan işlemlerin önbellekleme gerektirdiği senaryoları ele alır.

Redis oturumlarıyla session fixation saldırıları nasıl ele alınır?

Kimlik doğrulamadan sonra oturum kimliğini yeniden oluşturun. Kullanıcı kimlik bilgilerini saklamadan önce req.session.regenerate() çağrısı, saldırganların kimlik doğrulama öncesi oturum kimliklerini kullanmasını önler.

typescript
// Secure login with session regeneration
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: 'Logged in successfully' });
    });
  });
}
Mülakat İpucu

Kıdemli adaylar önbellekleme stratejileri arasındaki değiş tokuşları açıklar. Bir junior "önbellekleme performansı artırır" diyebilir. Bir senior, önbelleklemenin ne zaman fayda olmadan karmaşıklık eklediğini açıklar, örneğin yüksek düzeyde kişiselleştirilmiş veya hızla değişen veriler için.

Performans İzleme ve Redis Metrikleri

Üretim uygulamaları önbellek performansına görünürlük gerektirir. Temel metrikler şunları içerir:

  • Hit rate: Önbellekten sunulan isteklerin yüzdesi. %80'in altında TTL veya anahtar stratejisi sorunlarını gösterir.
  • Bellek kullanımı: Redis INFO memory komutu mevcut ve en yüksek belleği gösterir.
  • Bağlantı sayısı: Çok fazla bağlantı eksik havuzlamayı gösterir.
  • Tahliye sayısı: Sıfır olmayan tahliyeler önbelleğin dolu olduğu ve girişleri düşürdüğü anlamına gelir.
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>);
  }
}

Bu metrikleri bir health check endpoint'i aracılığıyla gösterin veya üretim izleme için Prometheus ile entegre edin.

Üretim NestJS Dağıtımlarında Redis Önbellekleme ve Oturumlar

Redis ile NestJS dağıtımı birkaç operasyonel endişeye dikkat gerektirir:

  • Bağlantı dizesi yönetimi: Redis URL'lerini kodda değil ortam değişkenlerinde saklayın. Parolalar için secrets management kullanın.
  • Zarif kapatma: Bağlantı sızıntılarını önlemek için uygulama kapatılırken Redis bağlantılarını kapatın.
  • Yeniden deneme mantığı: Redis bağlantı hataları için üstel geri çekilme yapılandırın.
  • Ayrı Redis örnekleri: Önbellek ve oturumlar için ayrı Redis örnekleri düşünün. Önbellek tahliyesi oturum verilerini etkilememelidir.
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();
  }
}

Mikroservis mimarileri için Redis, NestJS'in yerleşik taşıma katmanı aracılığıyla mesaj aracısı olarak da hizmet eder ve önbellekleme, oturumlar ve servisler arası iletişimi birleştirir.

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Günün meydan okuması

Node.js / NestJS kodundaki hatayı bulabilir misin?

Gerçek bir kod parçası, gizli bir hata, günde bir deneme. Denemek için hesap gerekmez.

Anthony Fillion-Maillet

Yazan:

Anthony Fillion-Maillet

SharpSkill kurucusu

10 yılı aşkın süredir fullstack geliştirici. SharpSkill’i yönetiyor ve burada yayımlanan her şeyden sorumlu.

10 Eylül 2026 tarihinde güncellendi

Paylaş

İlgili makaleler