مقدمة: لماذا التخزين المؤقت (Caching) ضروري لتطبيقات TypeScript
تواجه تطبيقات الويب الحديثة المكتوبة بـ TypeScript أحمالاً متزايدة ومتطلبات سرعة استجابة عالية. التخزين المؤقت (Caching) ليس مجرد خيار، بل هو ضرورة لأي تطبيق في مرحلة الإنتاج (production-grade). بدونه، يتحول كل طلب إلى قاعدة البيانات أو واجهة برمجة تطبيقات خارجية (API) إلى عملية مكلفة، تزيد من زمن الاستجابة بمقدار 10 إلى 100 مرة.
في هذه المقالة، سنستعرض ثلاثة أساليب رئيسية للتخزين المؤقت في TypeScript: Redis — التخزين المؤقت الموزع، Memcached — البديل خفيف الوزن، و الحلول المدمجة (in-memory cache). ستتعلم كيفية اختيار الأداة المناسبة وتنفيذها مع أمثلة أكواد.
1. التخزين المؤقت المدمج: In-Memory Cache في TypeScript
الطريقة الأبسط والأسرع هي تخزين البيانات مباشرة في الذاكرة العشوائية (RAM) للعملية. بالنسبة لـ TypeScript/Node.js، هذا مثالي للتطبيقات المتجانسة (monolithic) أو الخدمات المصغرة (microservices) ذات النسخة الواحدة (single instance).
تنفيذ كاش بسيط في الذاكرة (in-memory cache)
لننشئ كلاس SimpleCache مع دعم TTL (مدة الحياة):
interface CacheEntry<T> {
value: T;
expiresAt: number;
}
class SimpleCache<T> {
private store = new Map<string, CacheEntry<T>>();
private defaultTTL: number; // بالميلي ثانية
constructor(defaultTTL: number = 60000) {
this.defaultTTL = defaultTTL;
}
set(key: string, value: T, ttl?: number): void {
const expiresAt = Date.now() + (ttl ?? this.defaultTTL);
this.store.set(key, { value, expiresAt });
}
get(key: string): T | null {
const entry = this.store.get(key);
if (!entry) return null;
if (Date.now() > entry.expiresAt) {
this.store.delete(key);
return null;
}
return entry.value;
}
has(key: string): boolean {
return this.get(key) !== null;
}
delete(key: string): void {
this.store.delete(key);
}
clear(): void {
this.store.clear();
}
}
// مثال على الاستخدام
const userCache = new SimpleCache<User>(30000); // TTL 30 ثانية
userCache.set('user:123', { id: 123, name: 'آنا' });
const cachedUser = userCache.get('user:123');
إيجابيات وسلبيات التخزين المؤقت في الذاكرة (in-memory cache)
- الإيجابيات: سرعة قصوى (نانو ثانية)، لا تأخير في الشبكة، سهولة في التنفيذ.
- السلبيات: فقدان البيانات عند إعادة تشغيل التطبيق، محدود بحجم RAM لعملية واحدة، لا يعمل مع التوسع الأفقي (horizontal scaling).
متى تستخدم: لتخزين نتائج العمليات الحسابية الثقيلة، الإعدادات (configurations)، البيانات الثابتة التي نادراً ما تتغير.
2. Redis: التخزين المؤقت الموزع لتطبيقات TypeScript
Redis هو مخزن البيانات في الذاكرة (in-memory data store) الأكثر شيوعاً، ويُستخدم كذاكرة تخزين مؤقت (cache)، ووسيط رسائل (message broker)، وقاعدة بيانات. بالنسبة لـ TypeScript، سنستخدم المكتبة ioredis.
التثبيت والإعداد الأساسي
npm install ioredis
npm install @types/ioredis -D
لننشئ خدمة للعمل مع Redis:
import Redis from 'ioredis';
class RedisCacheService {
private client: Redis;
private defaultTTL: number;
constructor(ttlSeconds: number = 3600) {
this.client = new Redis({
host: process.env.REDIS_HOST || 'localhost',
port: Number(process.env.REDIS_PORT) || 6379,
retryStrategy: (times) => Math.min(times * 50, 2000),
});
this.defaultTTL = ttlSeconds;
}
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
const serialized = JSON.stringify(value);
const ttlToUse = ttl ?? this.defaultTTL;
await this.client.setex(key, ttlToUse, serialized);
}
async get<T>(key: string): Promise<T | null> {
const data = await this.client.get(key);
if (!data) return null;
try {
return
```