Runtime Memory Cache

A lightweight, high-performance in-memory cache for Node.js with TTL support, automatic cleanup, memory usage tracking, and zero dependencies.

Fast Performance

O(1) lookups using native JavaScript Map with zero dependencies

TTL Support

Time To Live with automatic expiration and manual cleanup

🔄

Eviction Policies

FIFO or LRU eviction with size limiting and skip touch

📊

Statistics Tracking

Optional stats for hits, misses, evictions, and performance

💾

Memory Tracking

Monitor memory usage with estimated bytes per entry

🔷

TypeScript

Full type definitions and TypeScript support built-in

🎮 Interactive Playground

Try out the cache features live! Set values, test eviction policies, and monitor statistics in real-time.

Cache Configuration

Set Value

Get / Check / Delete

Cache Operations

Cache Keys (0)

No keys in cache

Statistics

Installation

npm install runtime-memory-cache

Quick Start

import RuntimeMemoryCache from 'runtime-memory-cache';

// Create cache with options
const cache = new RuntimeMemoryCache({
  ttl: 60000,           // 1 minute default TTL
  maxSize: 1000,        // Maximum 1000 entries
  enableStats: true,    // Enable statistics tracking
  evictionPolicy: 'LRU' // Use LRU eviction policy
});

// Store data
cache.set('user:123', { name: 'John', age: 30 });

// Retrieve data
const user = cache.get('user:123');
console.log(user); // { name: 'John', age: 30 }

// Check if key exists
if (cache.has('user:123')) {
  console.log('User exists!');
}

Constructor Options

interface CacheOptions {
  ttl?: number;              // Default TTL in milliseconds
  maxSize?: number;          // Maximum cache entries (default: 1000)
  enableStats?: boolean;     // Enable statistics tracking (default: false)
  evictionPolicy?: 'FIFO' | 'LRU';  // Eviction policy (default: 'FIFO')
}
Option Type Default Description
ttl number undefined Default TTL for cache entries in milliseconds
maxSize number 1000 Maximum number of entries in cache
enableStats boolean false Enable statistics tracking
evictionPolicy 'FIFO' | 'LRU' 'FIFO' Cache eviction policy

API Methods

set(key: string, value: any, ttl?: number): void

Store a value with optional TTL override.

cache.set('key', 'value');                // Uses default TTL
cache.set('key', 'value', 30000);          // 30 second TTL
cache.set('key', 'value', undefined);      // No expiration

get(key: string): any

Retrieve a value. Returns undefined if key doesn't exist or has expired.

const value = cache.get('key');
if (value !== undefined) {
  console.log('Found:', value);
}

has(key: string, skipTouch?: boolean): boolean

Check if key exists and is not expired. Optionally skip updating access time.

💡 Skip Touch Feature

Set skipTouch: true to check existence without affecting LRU order.

// Normal check - updates access time for LRU
if (cache.has('key')) {
  console.log('Key exists');
}

// Skip touch - doesn't affect LRU order
if (cache.has('key', true)) {
  console.log('Key exists, LRU order unchanged');
}

del(key: string): boolean

Delete a specific key. Returns true if key existed.

const wasDeleted = cache.del('key');
if (wasDeleted) {
  console.log('Key was deleted');
}

size(): number

Get current number of entries in cache.

console.log(`Cache has ${cache.size()} entries`);

clear(): void

Remove all entries from cache.

cache.clear();
console.log(cache.size()); // 0

keys(): string[]

Get array of all keys in cache.

const allKeys = cache.keys();
console.log('All keys:', allKeys);

cleanup(): number

Manually remove expired entries. Returns number of entries removed.

const removedCount = cache.cleanup();
console.log(`Removed ${removedCount} expired entries`);

getStats(): CacheStats | null

Get cache statistics (if enabled).

interface CacheStats {
  hits: number;        // Cache hits
  misses: number;      // Cache misses  
  size: number;        // Current cache size
  maxSize: number;     // Maximum allowed size
  evictions: number;   // Number of evicted entries
}
const stats = cache.getStats();
if (stats) {
  console.log(`Hits: ${stats.hits}, Misses: ${stats.misses}`);
  console.log(`Hit Rate: ${(stats.hits / (stats.hits + stats.misses) * 100).toFixed(2)}%`);
}

resetStats(): void

Reset statistics counters (if enabled).

cache.resetStats();
console.log(cache.getStats()); // All counters reset to 0

getEvictionPolicy(): 'FIFO' | 'LRU'

Get the current eviction policy being used.

console.log(cache.getEvictionPolicy()); // 'FIFO' or 'LRU'

getMemoryUsage(): MemoryUsage

Get estimated memory usage of the cache.

interface MemoryUsage {
  estimatedBytes: number;        // Total estimated bytes
  averageBytesPerEntry: number;  // Average bytes per entry
}
const memInfo = cache.getMemoryUsage();
console.log(`Cache uses ~${memInfo.estimatedBytes} bytes`);
console.log(`Average: ${memInfo.averageBytesPerEntry} bytes per entry`);

Eviction Policies

FIFO (First In, First Out) - Default

  • Removes the oldest inserted entry first
  • Simple and predictable behavior
  • Good for time-based caching scenarios
  • Tracks access time for consistency
const fifoCache = new RuntimeMemoryCache({ 
  maxSize: 100, 
  evictionPolicy: 'FIFO' 
});

LRU (Least Recently Used)

  • Removes the entry that hasn't been accessed for the longest time
  • Better cache hit rates for access-pattern-based scenarios
  • Uses access time tracking for eviction decisions
  • Updates on get(), has(), and set()
  • Skip Touch: Use has(key, true) to check without affecting order
const lruCache = new RuntimeMemoryCache({ 
  maxSize: 100, 
  evictionPolicy: 'LRU' 
});

📝 Both policies:

  • Automatically remove entries when cache is full
  • Track eviction count in statistics
  • Maintain O(1) average performance
  • Update access time for consistency

Usage Examples

API Response Caching

const apiCache = new RuntimeMemoryCache({ 
  ttl: 300000,    // 5 minutes
  maxSize: 1000,
  enableStats: true 
});

async function fetchUser(userId: string) {
  const cacheKey = `user:${userId}`;
  
  // Try cache first
  let user = apiCache.get(cacheKey);
  if (user) return user;
  
  // Fetch from API and cache
  user = await fetch(`/api/users/${userId}`).then(r => r.json());
  apiCache.set(cacheKey, user);
  
  return user;
}

Session Management

const sessionCache = new RuntimeMemoryCache({ 
  ttl: 1800000,  // 30 minutes
  maxSize: 10000 
});

function createSession(userId: string, data: any): string {
  const sessionId = generateId();
  sessionCache.set(`session:${sessionId}`, { userId, ...data });
  return sessionId;
}

function getSession(sessionId: string) {
  return sessionCache.get(`session:${sessionId}`);
}

Rate Limiting

const rateLimiter = new RuntimeMemoryCache({ ttl: 60000 }); // 1 minute window

function checkRateLimit(clientId: string, maxRequests: number = 100): boolean {
  const key = `rate:${clientId}`;
  const current = rateLimiter.get(key) || 0;
  
  if (current >= maxRequests) {
    return false; // Rate limit exceeded
  }
  
  rateLimiter.set(key, current + 1);
  return true;
}

Skip Touch Feature with LRU Cache

const lruCache = new RuntimeMemoryCache({ 
  maxSize: 3,
  evictionPolicy: 'LRU',
  enableStats: true 
});

// Fill cache to capacity
lruCache.set('user:1', 'Alice');
lruCache.set('user:2', 'Bob');
lruCache.set('user:3', 'Charlie');

// Check existence without affecting LRU order
if (lruCache.has('user:1', true)) {
  console.log('User 1 exists, but LRU order unchanged');
}

// Normal check that updates LRU order
if (lruCache.has('user:2')) {
  console.log('User 2 exists and moved to most recent');
}

// Add new user - 'user:1' will be evicted (least recently used)
// because it wasn't "touched" by the skipTouch check
lruCache.set('user:4', 'David');

console.log(lruCache.has('user:1')); // false - evicted
console.log(lruCache.has('user:2')); // true - recently accessed

Performance

O(1) average case for get, set, has, and delete operations

💾

Memory efficient with automatic garbage collection

📦

Zero dependencies - no external libraries

🔷

TypeScript optimized with proper type inference

Built with ❤️ by Surya Teja

Licensed under MIT