NestJS та Redis у 2026: Кешування, Сесії та Питання на Співбесідах
Повний посібник з інтеграції Redis у NestJS. Cache-manager, управління сесіями та стратегії інвалідації кешу.

Інтеграція Redis з NestJS трансформує продуктивність застосунку, замінюючи запити до бази даних швидкими зверненнями до пам'яті. Комбінація @nestjs/cache-manager для кешування та express-session з connect-redis для збереження сесій охоплює два найпоширеніших випадки використання Redis у бекенд-застосунках.
Кешування Redis у NestJS зменшує навантаження на базу даних, зберігаючи часто використовувані дані в пам'яті. Управління сесіями з Redis дозволяє горизонтальне масштабування завдяки спільному використанню стану сесій між кількома екземплярами.
Налаштування @nestjs/cache-manager з Redis
Модуль cache-manager був винесений в окремий пакет у NestJS 10. Встановлення вимагає як обгортки NestJS, так і базового сховища кешу. Адаптер @keyv/redis є рекомендованим підходом для інтеграції Redis з cache-manager v5.
# Install required packages
npm install @nestjs/cache-manager cache-manager @keyv/redisРеєстрація модуля налаштовує з'єднання з Redis та TTL за замовчуванням. Опція isGlobal робить кеш доступним у всіх модулях без повторного імпорту.
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 приймає кілька сховищ кешу для багаторівневого кешування. Продакшн-середовища зазвичай використовують Redis як основне сховище з опціональним fallback до локальної пам'яті для розробки.
Впровадження Кешу та Кешування на Рівні Сервісу
Токен CACHE_MANAGER забезпечує прямий доступ до кешу для операцій на рівні сервісу. Цей підхід пропонує більше контролю, ніж автоматичне HTTP-кешування, коли бізнес-логіка визначає інвалідацію кешу.
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} дозволяє цільову інвалідацію. Складніші патерни на кшталт user:* вимагають команд, специфічних для Redis, через базовий клієнт.
CacheInterceptor для Автоматичного Кешування HTTP-відповідей
Вбудований CacheInterceptor автоматично кешує HTTP GET-відповіді. Застосування на рівні контролера кешує всі GET-ендпоїнти, тоді як застосування на рівні методу забезпечує гранулярний контроль.
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);
}
}Інтерцептор за замовчуванням генерує ключі кешу з URL запиту. Кастомні декоратори @CacheKey() перевизначають цю поведінку для маршрутів, де параметри запиту не повинні впливати на кешування. Концепції інтерцепторів та гардів у NestJS часто з'являються на технічних співбесідах.
Ключ кешу за замовчуванням використовує повний URL, включаючи параметри запиту. Два запити до /products?page=1 та /products?page=2 створюють окремі записи в кеші. Перевизначте за допомогою @CacheKey(), коли пагінація не повинна створювати дубльовані кешовані відповіді.
Управління Сесіями з Redis та express-session
NestJS за замовчуванням працює на Express, що робить express-session стандартним вибором для управління сесіями. Адаптер connect-redis зберігає дані сесій у Redis замість пам'яті, забезпечуючи збереження сесій під час перезапуску сервера.
# Install session packages
npm install express-session connect-redis redis
npm install -D @types/express-sessionКонфігурація сесій відбувається в main.ts перед запуском застосунку. Redis-клієнт з'єднується незалежно від налаштування cache-manager.
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 запобігає збереженню порожніх сесій, зменшуючи використання пам'яті Redis. Налаштування secure: true для кукі вимагає HTTPS у продакшні.
Готовий до співбесід з Node.js / NestJS?
Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.
Доступ до Сесій у Контролерах та Гардах
Дані сесії доступні через об'єкт request. Типізований інтерфейс сесії покращує досвід розробника та виявляє помилки під час компіляції.
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;
}
}Розширений тип сесії робить властивості сесії доступними з автодоповненням.
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' });
});
});
}
}Гард захищає маршрути, перевіряючи валідність сесії. Цей патерн плавно інтегрується з системою авторизації та RBAC NestJS.
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
Інвалідація кешу — це складніша проблема в кешуванні. Три стратегії застосовуються до різних сценаріїв:
Закінчення часу (TTL) працює для даних, які змінюються передбачувано, або коли незначна застарілість прийнятна. Каталоги продуктів, значення конфігурації та публічний контент відповідають цьому патерну.
Інвалідація на основі подій очищає записи кешу, коли базові дані змінюються. Це вимагає явних викликів cache.del() в операціях оновлення та видалення.
Інвалідація на основі патернів видаляє кілька пов'язаних ключів. Redis підтримує видалення за патерном через команди SCAN та DEL, але cache-manager абстрагує це.
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() очищає всі кешовані дані, корисний під час розгортань або міграцій даних, але небезпечний у продакшні при випадковому виклику.
Пулінг З'єднань Redis та Підтримка Кластера
Продакшн-розгортання вимагають пулінгу з'єднань для ефективної обробки паралельних запитів. Бібліотека ioredis надає підтримку кластера та пулінг з'єднань одразу.
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 або Prisma, з'єднання з базою даних та кешем мають бути налаштовані окремо з власними пулами з'єднань.
Питання на Співбесідах з NestJS та Redis
Технічні співбесіди з кешування Node.js охоплюють як концептуальне розуміння, так і практичну реалізацію. Ці питання зустрічаються часто:
Чому використовувати Redis замість кешування в пам'яті?
Кешування в пам'яті не зберігається між перезапусками і не може бути спільним між кількома екземплярами. Redis забезпечує персистентність, реплікацію та спільний стан для горизонтально масштабованих застосунків. Компроміс — це мережева затримка порівняно з доступом до локальної пам'яті.
Чим cache-manager відрізняється від прямого використання Redis?
Бібліотека cache-manager надає уніфікований API для різних сховищ кешу. Перехід з пам'яті на Redis вимагає зміни конфігурації сховища без модифікації коду сервісу. Пряме використання Redis пропонує більше Redis-специфічних можливостей, як pub/sub, sorted sets та скрипти Lua.
Що таке cache stampede і як цьому запобігти?
Cache stampede виникає, коли популярний запис кешу закінчується і багато паралельних запитів одночасно звертаються до бази даних. Стратегії запобігання включають:
- Оновлення з блокуванням: Один запит оновлює кеш, поки інші чекають
- Ймовірнісне раннє закінчення: Випадкове оновлення до закінчення TTL
- Фонове оновлення: Окремий процес оновлює записи, що закінчуються
Коли використовувати CacheInterceptor проти кешування на рівні сервісу?
CacheInterceptor підходить для stateless GET-ендпоїнтів, де відповідь залежить лише від URL. Кешування на рівні сервісу обробляє сценарії, де інвалідація кешу має відбуватися при зміні даних або коли операції, відмінні від GET, потребують кешування.
Як обробляти атаки session fixation з Redis-сесіями?
Потрібно регенерувати ID сесії після автентифікації. Виклик req.session.regenerate() перед збереженням даних користувача запобігає використанню зловмисниками ID сесії до автентифікації.
// 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' });
});
});
}Сеньйорні кандидати пояснюють компроміси між стратегіями кешування. Джуніор може сказати "кешування покращує продуктивність". Сеньйор пояснює, коли кешування додає складність без користі, наприклад, для високо персоналізованих або швидко змінюваних даних.
Моніторинг Продуктивності та Метрики Redis
Продакшн-застосунки потребують видимості продуктивності кешу. Ключові метрики включають:
- Hit rate: Відсоток запитів, обслужених з кешу. Нижче 80% вказує на проблеми з TTL або стратегією ключів.
- Використання пам'яті: Команда Redis
INFO memoryпоказує поточне та пікове використання пам'яті. - Кількість з'єднань: Занадто багато з'єднань вказує на відсутність пулінгу.
- Кількість виселень: Ненульові виселення означають, що кеш заповнений і видаляє записи.
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>);
}
}Варто експонувати ці метрики через health check ендпоїнт або інтегрувати з Prometheus для продакшн-моніторингу.
Redis-кешування та Сесії в Продакшн-розгортаннях NestJS
Розгортання NestJS з Redis вимагає уваги до кількох операційних аспектів:
- Управління connection string: Зберігайте URL Redis у змінних середовища, не в коді. Використовуйте secrets management для паролів.
- Graceful shutdown: Закривайте з'єднання Redis під час зупинки застосунку, щоб запобігти витоку з'єднань.
- Логіка повторних спроб: Налаштуйте exponential backoff для помилок з'єднання Redis.
- Окремі екземпляри Redis: Розгляньте окремі екземпляри Redis для кешу та сесій. Виселення кешу не повинно впливати на дані сесій.
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();
}
}Для мікросервісних архітектур Redis також слугує брокером повідомлень через вбудований транспортний шар NestJS, об'єднуючи кешування, сесії та міжсервісну комунікацію.
Починай практикувати!
Перевір свої знання з нашими симуляторами співбесід та технічними тестами.
Чи знайдеш ти помилку в Node.js / NestJS?
Справжній фрагмент коду, прихована помилка, одна спроба на день. Щоб спробувати, акаунт не потрібен.

Автор:
Anthony Fillion-MailletЗасновник SharpSkill
Fullstack-розробник понад 10 років. Керує SharpSkill і відповідає за все, що тут публікується.
Оновлено 10 вересня 2026 р.
Поділитися
Пов'язані статті

Швидкий JavaScript ListView у 2026: Windowing, Віртуалізація та Оптимізація Продуктивності
Опануйте техніки windowing та віртуалізації для рендерингу тисяч елементів у JavaScript. TanStack Virtual, react-virtuoso та найкращі практики для ефективних списків у 2026 році.

Високопродуктивний JavaScript ListView у 2026: Техніки віртуалізації та оптимізації
Опануйте віртуалізацію списків JavaScript з TanStack Virtual, react-virtuoso та react-window. Рендеріть понад 100 тисяч елементів плавно завдяки технікам windowing.

NestJS та MongoDB у 2026: Mongoose, Агрегації та Питання на Співбесідах
Опануйте NestJS з MongoDB та Mongoose 9. Вивчіть патерни проектування схем, конвеєри агрегацій та підготуйтесь до технічних співбесід з практичними прикладами.