Caching in TypeScript: Redis and other tools
Caching is one of the key strategies for optimizing web application performance. In the TypeScript and Node.js ecosystem, there are several powerful libraries that allow you to efficiently manage data caching. In this article, we will take a detailed look at popular tools, their application, and best practices.
Why is caching needed?
Caching allows you to temporarily store frequently requested data in fast storage, so you don't have to perform expensive operations (database queries, computations, external APIs) every time. This reduces server load and decreases response time.
Main tools for caching in TypeScript
1. Redis via the ioredis library
Redis is a high-performance in-memory data store that supports various data structures. For TypeScript, the best choice is ioredis — a client with full support for types and promises.
import Redis from 'ioredis';
// Create a clientconst redis = new Redis({ host: 'localhost', port: 6379,});
// Function for caching with TTLasync function cacheGet(key: string): Promise<string | null> { const data = await redis.get(key); return data;}
async function cacheSet(key: string, value: string, ttl: number = 3600): Promise<void> { await redis.setex(key, ttl, value);}
// Usage exampleasync function getUserData(userId: string) { const cacheKey = `user:${userId}`; const cached = await cacheGet(cacheKey); if (cached) { return JSON.parse(cached); } // Emulating an expensive request const userData = { id: userId, name: 'John Doe' }; await cacheSet(cacheKey, JSON.stringify(userData), 3600); return userData;}2. node-cache — in-memory cache for small projects
node-cache is a simple in-memory library that requires no external dependencies. It is ideal for prototypes and microservices with a small amount of data.
import NodeCache from 'node-cache';
const myCache = new NodeCache({ stdTTL: 600, checkperiod: 120 });
// Save datamyCache.set('key', { data: 'value' });
// Get dataconst value = myCache.get('key');if (value) { console.log('From cache:', value);} else { console.log('Cache is empty, requesting data...');}
// Delete by keymyCache.del('key');
// Clear entire cachemyCache.flushAll();3. async-cache-dedupe — for preventing duplicate requests
This library solves the "cache stampede" problem (when many requests try to fill the cache simultaneously). It automatically deduplicates parallel requests to the same key.
import { createCache } from 'async-cache-dedupe';
const cache = createCache({ ttl: 60, // time to live in seconds stale: 10, // time when data is considered stale but can still be served});
// Wrap an async functionconst getData = cache.define('getData', async (id: number) => { // Expensive request const result = await fetch(`https://api.example.com/data/${id}`); return result.json();});
// Use itconst result = await getData(42);Cache invalidation strategies
One of the most challenging tasks in caching is invalidation — updating stale data. Let's look at the main approaches:
- TTL (Time-To-Live) — data is automatically deleted after a specified time. A simple and reliable method.
- Lazy invalidation — check data relevance on each request (e.g., by version).
- Event-driven invalidation — when data changes in the database, send an event to clear the corresponding cache.
- Write-through — update the cache simultaneously with writing to the main
Also in library
Working with S3 and Cloud Storage in Go: minio-go Library
Generating PDF Reports in Rust: Libraries and Practical Examples
Reactive Programming in Swift: Combine and RxSwift for iOS/macOS
Redis client for C++: connection, installation, and examples
Working with CSV in Swift (iOS/macOS): SwiftCSV Library and Parsing Basics