Caching in JavaScript: Redis, Memcached, and Built-in Solutions: From A to Z
In modern web development with Node.js, application performance directly depends on the speed of data access. Every request to a database, external API, or file system creates a delay that accumulates as load increases. Caching is a strategy of temporarily storing frequently requested data in fast storage, allowing response time to be reduced by 10–100 times. In this article, we will cover three approaches to caching in JavaScript: built-in solutions (Map, WeakMap, the node-cache module), distributed systems Redis and Memcached, as well as their combinations.
Why is caching needed in Node.js?
The main goal of caching is to reduce the load on slow resources (databases, disks, external services) and speed up data delivery to the user. In a typical Node.js web application, the cache can store:
- Results of complex database queries (aggregations, JOINs).
- Responses from external APIs (weather, exchange rates, geodata).
- User sessions and authentication tokens.
- HTML fragments or entire pages (SSR caching).
- Configuration files and templates.
Without caching, every repeated access to the same data leads to duplicated work. With a cache, data is retrieved from RAM in microseconds.
Built-in Solutions for Caching in JavaScript
For simple scenarios or single-threaded applications, you can use native JavaScript data structures. They do not require installing external packages, but have limitations on size and lifetime.
1. Map and WeakMap
The Map object is a key-value collection, ideally suited for caching within a single process. Example of a simple cache with TTL (time-to-live):
class SimpleCache { constructor(ttl = 60000) { this.cache = new Map(); this.ttl = ttl; }
set(key, value) { this.cache.set(key, { value, expiry: Date.now() + this.ttl }); }
get(key) { const item = this.cache.get(key); if (!item) return null; if (Date.now() > item.expiry) { this.cache.delete(key); return null; } return item.value; }
has(key) { return this.get(key) !== null; }}
// Usageconst cache = new SimpleCache(5000); // TTL 5 secondscache.set('user:123', { name: 'Ivan' });console.log(cache.get('user:123')); // { name: 'Ivan' }WeakMap differs in that its keys are only objects, and the garbage collector can delete entries if there are no more references to the key object. This is convenient for caching DOM elements or temporary objects.
2. The node-cache Module
For a production environment, it is better to use the ready-made node-cache library. It supports TTL, automatic cleanup, and works faster than a custom solution:
const NodeCache = require('node-cache');const myCache = new NodeCache({ stdTTL: 100, checkperiod: 120 });
// WritemyCache.set('key', { data: 'value' });
// Readconst value = myCache.get('key');if (value === undefined) { // Cache miss — need to get data from DB}
// DeletemyCache.del('key');Built-in solutions are suitable for monolithic applications or microservices with a single instance. However, when scaling to multiple processes or servers, data becomes inconsistent — distributed caches are needed here.
Redis: A Powerful In-Memory Data Store
Redis is a high-performance in-memory data store supporting strings, hashes, lists, sets, and other structures. For Node.js, the redis or ioredis package is used. Redis is ideal for caching due to:
- Support for TTL at the key level (
EXPIREcommand). - Atomic operations (INC