Caching with Arctickey

Arctickey is perfect for application caching — reduce database load, speed up API responses, and improve user experience.

Why Use Redis for Caching?#

  • Sub-millisecond latency — faster than any database query
  • Automatic expiration — set TTL and forget
  • Memory-efficient — only store what you need
  • Atomic operations — no race conditions

Basic Caching Pattern#

TypeScript
import Redis from 'ioredis'; const redis = new Redis('rediss://:YOUR_PASSWORD@YOUR_INSTANCE.eu.arctickey.com:6379'); async function getCachedData(key: string, fetchFn: () => Promise<any>, ttlSeconds = 300) { // Try cache first const cached = await redis.get(key); if (cached) { return JSON.parse(cached); } // Fetch fresh data const data = await fetchFn(); // Store in cache with TTL await redis.setex(key, ttlSeconds, JSON.stringify(data)); return data; } // Usage const user = await getCachedData( `user:${userId}`, () => db.users.findById(userId), 600 // 10 minutes );

Cache-Aside Pattern#

The most common pattern — check cache, fetch if missing, store result:

TypeScript
async function getProduct(id: string) { const cacheKey = `product:${id}`; // 1. Check cache const cached = await redis.get(cacheKey); if (cached) return JSON.parse(cached); // 2. Cache miss - fetch from database const product = await db.products.findById(id); if (!product) return null; // 3. Store in cache await redis.setex(cacheKey, 3600, JSON.stringify(product)); return product; }

Write-Through Pattern#

Update cache and database together:

TypeScript
async function updateProduct(id: string, data: ProductData) { // Update database const product = await db.products.update(id, data); // Update cache immediately await redis.setex(`product:${id}`, 3600, JSON.stringify(product)); return product; }

TTL Strategies#

Choose TTL based on data characteristics:

Data TypeSuggested TTLReason
User profile5-15 minChanges occasionally
Product listing1-5 minInventory changes
Static content1-24 hoursRarely changes
API responses30-60 secBalance freshness/load
Session data24-48 hoursUser convenience
TypeScript
// Short TTL for frequently changing data await redis.setex('stock:product123', 30, '42'); // Long TTL for static content await redis.setex('footer:html', 86400, footerHtml); // No TTL for critical data (manual invalidation) await redis.set('config:features', JSON.stringify(features));

Cache Invalidation#

Delete on Update#

TypeScript
async function updateUser(id: string, data: UserData) { await db.users.update(id, data); await redis.del(`user:${id}`); }

Pattern-Based Invalidation#

TypeScript
// Invalidate all product caches const keys = await redis.keys('product:*'); if (keys.length > 0) { await redis.del(...keys); }

Tag-Based Invalidation#

TypeScript
// Store tags with cached items await redis.sadd('tag:electronics', 'product:123', 'product:456'); // Invalidate by tag const keys = await redis.smembers('tag:electronics'); if (keys.length > 0) { await redis.del(...keys); await redis.del('tag:electronics'); }

Next.js Data Cache#

TypeScript
// app/api/products/route.ts import { unstable_cache } from 'next/cache'; const getProducts = unstable_cache( async () => { return await db.products.findMany(); }, ['products'], { revalidate: 60 } // Revalidate every 60 seconds );

Best Practices#

  1. Use consistent key namingentity:id:field format
  2. Set appropriate TTLs — don't cache forever
  3. Handle cache failures gracefully — fall back to database
  4. Monitor hit rates — aim for >90% cache hits
  5. Avoid caching sensitive data — or use encryption