DEV Community

Wallace Freitas
Wallace Freitas

Posted on

1

Top 5 Caching Patterns for High-Performance Applications

Caching is a powerful technique to enhance the performance and scalability of applications. By storing frequently accessed data in faster storage layers, you can reduce latency, alleviate database load, and provide a smoother user experience.

In this article, we’ll explore the top five caching patterns that every developer should know. Using TypeScript and Node.js, we’ll demonstrate how these patterns can be implemented to optimize application performance.

1️⃣ In-Memory Cache
An in-memory cache stores data in memory for fast read and write operations. It is ideal for storing small, frequently accessed data like session information or configuration settings.

Example: Caching with Node.js

class InMemoryCache<T> {
  private cache: Map<string, T> = new Map();

  set(key: string, value: T) {
    this.cache.set(key, value);
  }

  get(key: string): T | undefined {
    return this.cache.get(key);
  }

  delete(key: string) {
    this.cache.delete(key);
  }
}

// Usage
const cache = new InMemoryCache<number>();
cache.set('user:123', 42);
console.log(cache.get('user:123')); // Output: 42
Enter fullscreen mode Exit fullscreen mode

2️⃣ Write-Through Cache

In a write-through cache, writes are first made to the cache and then immediately written to the underlying data store. This ensures that the cache is always in sync with the database.

Example: Write-Through Cache

class WriteThroughCache<T> {
  private cache: Map<string, T> = new Map();

  async write(key: string, value: T, writeToDb: (key: string, value: T) => Promise<void>) {
    this.cache.set(key, value);
    await writeToDb(key, value); // Write to database
  }

  get(key: string): T | undefined {
    return this.cache.get(key);
  }
}

// Simulated database function
async function dbWrite(key: string, value: any) {
  console.log(`Writing ${key}: ${value} to database`);
}

// Usage
const writeCache = new WriteThroughCache<string>();
writeCache.write('key1', 'value1', dbWrite);
console.log(writeCache.get('key1')); // Output: 'value1'
Enter fullscreen mode Exit fullscreen mode

3️⃣ Cache-aside Pattern

In the cache-aside pattern, the application checks the cache first. If the data is not available, it retrieves it from the database and stores it in the cache for future requests.

Example: Cache-aside with Redis

import redis from 'redis';
import { promisify } from 'util';

const client = redis.createClient();
const getAsync = promisify(client.get).bind(client);
const setAsync = promisify(client.set).bind(client);

async function getData(key: string, fetchFromDb: () => Promise<string>): Promise<string> {
  const cachedValue = await getAsync(key);

  if (cachedValue) {
    console.log('Cache hit');
    return cachedValue;
  }

  console.log('Cache miss');
  const value = await fetchFromDb();
  await setAsync(key, value);
  return value;
}

// Simulated database fetch
async function fetchFromDb() {
  return 'Database Value';
}

// Usage
getData('user:123', fetchFromDb).then(console.log);
Enter fullscreen mode Exit fullscreen mode

4️⃣ Read-Through Cache

In a read-through cache, all read requests go through the cache. If the data is not present, the cache fetches it from the data store and updates itself automatically.

Example: Read-Through Cache

class ReadThroughCache<T> {
  private cache: Map<string, T> = new Map();

  async get(key: string, fetchFromDb: () => Promise<T>): Promise<T> {
    if (this.cache.has(key)) {
      console.log('Cache hit');
      return this.cache.get(key)!;
    }

    console.log('Cache miss');
    const value = await fetchFromDb();
    this.cache.set(key, value);
    return value;
  }
}

// Usage
const readCache = new ReadThroughCache<string>();
readCache.get('user:123', async () => 'Fetched from DB').then(console.log);
Enter fullscreen mode Exit fullscreen mode

5️⃣ Distributed Cache

A distributed cache stores data across multiple nodes, ensuring scalability and fault tolerance. It is commonly used in large-scale systems to handle high traffic efficiently.

Example: Using Redis as a Distributed Cache

import { createClient } from 'redis';

const client = createClient();

async function setCache(key: string, value: string) {
  await client.connect();
  await client.set(key, value);
  console.log(`Set ${key}: ${value}`);
  await client.disconnect();
}

async function getCache(key: string) {
  await client.connect();
  const value = await client.get(key);
  console.log(`Get ${key}: ${value}`);
  await client.disconnect();
  return value;
}

// Usage
setCache('session:abc', 'active');
getCache('session:abc');
Enter fullscreen mode Exit fullscreen mode

Caching patterns are essential for building high-performance, scalable applications. Whether you’re using simple in-memory caches or complex distributed caching solutions, choosing the right pattern for your use case can significantly improve your application’s speed and reliability.

List of strategies of cache with diagrams

By implementing these patterns in Node.js with TypeScript, you can optimize your system's performance and enhance user experience. Start with the basics and scale as your application's needs evolve.

Happy coding ❤️

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay