memory-cache

Node.js cachein-memory cachelightweightsimpleJavaScript

GitHub Overview

dotnet/runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.

Stars17,028
Watchers458
Forks5,203
Created:September 24, 2019
Language:C#
License:MIT License

Topics

dotnethacktoberfesthelp-wanted

Star History

dotnet/runtime Star History
Data as of: 10/22/2025, 10:03 AM

Library

memory-cache

Overview

memory-cache is a simple in-memory cache library for Node.js. It provides lightweight key-value storage through basic put/get/del methods, achieving fast data access with a minimal feature set. As of 2025, version 0.2.0 remains the latest version with its last update 8 years ago, yet it continues to be used by 730+ projects and serves as a reliable choice for simple caching needs. While adoption for new projects has declined due to the rise of modern alternatives, it maintains a solid track record of stable operation in existing projects.

Details

memory-cache 0.2.0 is a stable version released 8 years ago, providing simple cache implementation that serves as a foundation for the Node.js ecosystem. As a pure JavaScript library without external dependencies, it features minimal design with no TTL functionality, no statistics, and no event capabilities. It achieves high-speed operation through direct Object manipulation and minimizes learning costs with an easy-to-understand API. Currently, high-featured alternatives like node-cache, lru-cache, and cache-manager have become mainstream, requiring careful consideration for new projects.

Key Features

  • Simple API: Intuitive operations through put(), get(), del()
  • Lightweight Implementation: Minimal codebase without external dependencies
  • Fast Access: High-speed data access through direct object manipulation
  • Immediate Use: Ready to use without configuration
  • Compatibility: Works with older Node.js versions
  • Predictability: Predictable behavior due to lack of complex features

Pros and Cons

Pros

  • Extremely simple and understandable API (minimal learning cost)
  • Immediate use without external dependencies
  • Lightweight and fast memory access (minimal overhead)
  • Reliability through 8 years of stable operation track record
  • High compatibility with older Node.js versions
  • Easy debugging (simple internal structure)

Cons

  • 8 years without updates (maintenance abandoned state)
  • No TTL functionality (no expiration management)
  • No statistics functionality (difficult monitoring/optimization)
  • No event functionality (no cache state change monitoring)
  • No memory leak protection (manual cleanup required)
  • No modern TypeScript support

Reference Pages

Code Examples

Installation and Basic Setup

# NPM installation
npm install memory-cache

# Yarn installation
yarn add memory-cache

# TypeScript definitions not included (separate type definitions required)
npm install --save-dev @types/memory-cache

Basic Cache Operations

const cache = require('memory-cache')

// Basic put/get operations
cache.put('user:1', { name: 'John', age: 30 })
const user = cache.get('user:1')
console.log(user) // { name: 'John', age: 30 }

// Set data with TTL (milliseconds)
cache.put('session:abc123', 'session_data', 5 * 60 * 1000) // 5 minutes

// Get non-existent key
const notFound = cache.get('nonexistent')
console.log(notFound) // null

// Delete key
cache.del('user:1')

// Clear all data
cache.clear()

// Get current key list
const keys = cache.keys()
console.log('Cache keys:', keys)

// Get cache size
const size = cache.size()
console.log('Cache size:', size)

// Check key existence (custom implementation)
function hasKey(key) {
  return cache.get(key) !== null
}

console.log('Has user:1:', hasKey('user:1'))

TTL Management and Cleanup System

const cache = require('memory-cache')

class TTLCacheWrapper {
  constructor() {
    this.cache = cache
    this.cleanupInterval = null
    this.setupCleanup()
  }

  // Setup periodic cleanup
  setupCleanup() {
    // Check for expired items every 5 minutes
    this.cleanupInterval = setInterval(() => {
      this.cleanup()
    }, 5 * 60 * 1000)
  }

  // Cleanup expired items
  cleanup() {
    const keys = this.cache.keys()
    let cleanedCount = 0
    
    keys.forEach(key => {
      // memory-cache automatically removes expired items,
      // so existence check is sufficient
      if (this.cache.get(key) === null) {
        cleanedCount++
      }
    })
    
    console.log(`Cleaned up ${cleanedCount} expired items`)
  }

  // Short-term cache (5 minutes)
  putShort(key, value) {
    return this.cache.put(key, value, 5 * 60 * 1000)
  }

  // Medium-term cache (30 minutes)
  putMedium(key, value) {
    return this.cache.put(key, value, 30 * 60 * 1000)
  }

  // Long-term cache (2 hours)
  putLong(key, value) {
    return this.cache.put(key, value, 2 * 60 * 60 * 1000)
  }

  // Permanent cache (no TTL)
  putPermanent(key, value) {
    return this.cache.put(key, value)
  }

  get(key) {
    return this.cache.get(key)
  }

  del(key) {
    return this.cache.del(key)
  }

  clear() {
    return this.cache.clear()
  }

  // Custom statistics
  getStats() {
    const keys = this.cache.keys()
    const validKeys = keys.filter(key => this.cache.get(key) !== null)
    
    return {
      totalKeys: keys.length,
      validKeys: validKeys.length,
      expiredKeys: keys.length - validKeys.length,
      memoryUsage: process.memoryUsage().heapUsed
    }
  }

  // Resource cleanup
  destroy() {
    if (this.cleanupInterval) {
      clearInterval(this.cleanupInterval)
      this.cleanupInterval = null
    }
    this.cache.clear()
  }
}

// Usage example
const ttlCache = new TTLCacheWrapper()

// Set data with different TTLs
ttlCache.putShort('temp:calc', { result: 42 })
ttlCache.putMedium('user:session', { userId: 123, authenticated: true })
ttlCache.putLong('config:app', { theme: 'dark', language: 'en' })
ttlCache.putPermanent('constants:pi', 3.14159)

// Check statistics
setInterval(() => {
  console.log('Cache Stats:', ttlCache.getStats())
}, 60000)

// Cleanup on application exit
process.on('SIGINT', () => {
  ttlCache.destroy()
  process.exit()
})

Express.js Integration

const express = require('express')
const cache = require('memory-cache')

const app = express()

// Cache middleware
function cacheMiddleware(duration = 5 * 60 * 1000) {
  return (req, res, next) => {
    const key = req.originalUrl || req.url
    const cached = cache.get(key)

    if (cached) {
      console.log(`Cache HIT: ${key}`)
      return res.json(cached)
    }

    console.log(`Cache MISS: ${key}`)

    // Response capture
    const originalJson = res.json
    res.json = function(data) {
      // Save response to cache
      cache.put(key, data, duration)
      console.log(`Cache SET: ${key} (TTL: ${duration}ms)`)
      
      originalJson.call(this, data)
    }

    next()
  }
}

// Conditional cache middleware
function conditionalCache(condition, duration = 5 * 60 * 1000) {
  return (req, res, next) => {
    if (condition && !condition(req)) {
      return next()
    }
    return cacheMiddleware(duration)(req, res, next)
  }
}

// API endpoints
app.get('/api/users', 
  cacheMiddleware(10 * 60 * 1000), // 10 minute cache
  async (req, res) => {
    // Simulate heavy processing
    await new Promise(resolve => setTimeout(resolve, 1000))
    
    const users = [
      { id: 1, name: 'John', email: '[email protected]' },
      { id: 2, name: 'Jane', email: '[email protected]' }
    ]
    
    res.json(users)
  }
)

// User-specific data cache
app.get('/api/user/:id', 
  conditionalCache(
    req => req.params.id && !isNaN(req.params.id),
    15 * 60 * 1000 // 15 minute cache
  ),
  async (req, res) => {
    const userId = req.params.id
    
    // Simulate database access
    await new Promise(resolve => setTimeout(resolve, 500))
    
    const user = {
      id: parseInt(userId),
      name: `User ${userId}`,
      email: `user${userId}@example.com`,
      lastAccess: new Date()
    }
    
    res.json(user)
  }
)

// Cache statistics endpoint
app.get('/cache/stats', (req, res) => {
  const keys = cache.keys()
  const validKeys = keys.filter(key => cache.get(key) !== null)
  
  res.json({
    totalKeys: keys.length,
    validKeys: validKeys.length,
    expiredKeys: keys.length - validKeys.length,
    keys: validKeys.slice(0, 10), // Show only first 10 keys
    memoryUsage: process.memoryUsage()
  })
})

// Cache clear endpoint
app.delete('/cache/:pattern?', (req, res) => {
  const pattern = req.params.pattern
  
  if (pattern) {
    // Partial deletion with pattern matching
    const keys = cache.keys()
    const matchedKeys = keys.filter(key => key.includes(pattern))
    
    matchedKeys.forEach(key => cache.del(key))
    
    res.json({
      message: `Cache cleared for pattern: ${pattern}`,
      clearedKeys: matchedKeys.length,
      clearedItems: matchedKeys
    })
  } else {
    // Clear all cache
    const beforeKeys = cache.keys().length
    cache.clear()
    
    res.json({
      message: 'All cache cleared',
      clearedKeys: beforeKeys
    })
  }
})

app.listen(3000, () => {
  console.log('Server running on port 3000')
  
  // Periodic cache monitoring
  setInterval(() => {
    const keys = cache.keys()
    console.log(`Cache status - Total keys: ${keys.length}`)
  }, 60000)
})

Backup and Restore Functionality

const cache = require('memory-cache')
const fs = require('fs')
const path = require('path')

class BackupCacheManager {
  constructor() {
    this.cache = cache
    this.backupFile = path.join(__dirname, 'cache-backup.json')
    this.autoBackupInterval = null
  }

  // Backup cache data
  backup() {
    try {
      const keys = this.cache.keys()
      const backupData = {
        timestamp: Date.now(),
        data: {}
      }

      keys.forEach(key => {
        const value = this.cache.get(key)
        if (value !== null) {
          backupData.data[key] = value
        }
      })

      fs.writeFileSync(this.backupFile, JSON.stringify(backupData, null, 2))
      console.log(`Cache backup completed: ${keys.length} items saved`)
      
      return {
        success: true,
        itemCount: Object.keys(backupData.data).length,
        file: this.backupFile
      }
    } catch (error) {
      console.error('Backup failed:', error)
      return { success: false, error: error.message }
    }
  }

  // Restore cache data
  restore() {
    try {
      if (!fs.existsSync(this.backupFile)) {
        throw new Error('Backup file not found')
      }

      const backupContent = fs.readFileSync(this.backupFile, 'utf8')
      const backupData = JSON.parse(backupContent)

      // Clear existing cache
      this.cache.clear()

      // Restore backup data
      let restoredCount = 0
      Object.entries(backupData.data).forEach(([key, value]) => {
        this.cache.put(key, value)
        restoredCount++
      })

      console.log(`Cache restore completed: ${restoredCount} items restored`)
      
      return {
        success: true,
        itemCount: restoredCount,
        backupTimestamp: backupData.timestamp
      }
    } catch (error) {
      console.error('Restore failed:', error)
      return { success: false, error: error.message }
    }
  }

  // Enable automatic backup
  enableAutoBackup(intervalMinutes = 30) {
    if (this.autoBackupInterval) {
      clearInterval(this.autoBackupInterval)
    }

    this.autoBackupInterval = setInterval(() => {
      console.log('Performing automatic backup...')
      this.backup()
    }, intervalMinutes * 60 * 1000)

    console.log(`Auto backup enabled: every ${intervalMinutes} minutes`)
  }

  // Disable automatic backup
  disableAutoBackup() {
    if (this.autoBackupInterval) {
      clearInterval(this.autoBackupInterval)
      this.autoBackupInterval = null
      console.log('Auto backup disabled')
    }
  }

  // Export cache to JSON format
  exportToJSON(filePath) {
    try {
      const keys = this.cache.keys()
      const exportData = {
        exportDate: new Date().toISOString(),
        totalItems: keys.length,
        items: {}
      }

      keys.forEach(key => {
        const value = this.cache.get(key)
        if (value !== null) {
          exportData.items[key] = {
            value: value,
            type: typeof value,
            exportedAt: Date.now()
          }
        }
      })

      fs.writeFileSync(filePath, JSON.stringify(exportData, null, 2))
      
      return {
        success: true,
        itemCount: Object.keys(exportData.items).length,
        file: filePath
      }
    } catch (error) {
      return { success: false, error: error.message }
    }
  }

  // Import cache from JSON format
  importFromJSON(filePath) {
    try {
      const importContent = fs.readFileSync(filePath, 'utf8')
      const importData = JSON.parse(importContent)

      let importedCount = 0
      Object.entries(importData.items || {}).forEach(([key, item]) => {
        this.cache.put(key, item.value)
        importedCount++
      })

      return {
        success: true,
        itemCount: importedCount,
        originalExportDate: importData.exportDate
      }
    } catch (error) {
      return { success: false, error: error.message }
    }
  }

  // Resource cleanup
  destroy() {
    this.disableAutoBackup()
  }
}

// Usage example
const backupManager = new BackupCacheManager()

// Set data
cache.put('user:1', { name: 'John', email: '[email protected]' })
cache.put('config:app', { theme: 'dark', version: '1.0' })
cache.put('session:abc', { userId: 1, loginTime: Date.now() })

// Backup and restore
const backupResult = backupManager.backup()
console.log('Backup result:', backupResult)

// Enable automatic backup (every 10 minutes)
backupManager.enableAutoBackup(10)

// Test export functionality
const exportResult = backupManager.exportToJSON('./cache-export.json')
console.log('Export result:', exportResult)

// Cleanup on process exit
process.on('SIGINT', () => {
  backupManager.backup() // Final backup
  backupManager.destroy()
  process.exit()
})

Migration Support to Modern Libraries

const memoryCache = require('memory-cache')

/**
 * Migration helper from memory-cache to node-cache
 */
class MigrationHelper {
  constructor() {
    this.oldCache = memoryCache
    this.migrationLog = []
  }

  // Prepare data migration
  prepareMigration() {
    const keys = this.oldCache.keys()
    const migrationData = []

    keys.forEach(key => {
      const value = this.oldCache.get(key)
      if (value !== null) {
        migrationData.push({
          key,
          value,
          type: typeof value,
          migrationTime: Date.now()
        })
      }
    })

    console.log(`Migration preparation completed: ${migrationData.length} items ready`)
    return migrationData
  }

  // Convert to node-cache format
  convertToNodeCache(migrationData) {
    const NodeCache = require('node-cache')
    const newCache = new NodeCache({ stdTTL: 600, checkperiod: 120 })

    migrationData.forEach(item => {
      newCache.set(item.key, item.value)
      this.migrationLog.push({
        action: 'migrated',
        key: item.key,
        timestamp: Date.now()
      })
    })

    console.log(`Migration to node-cache completed: ${migrationData.length} items`)
    return newCache
  }

  // Convert to lru-cache format
  convertToLRUCache(migrationData) {
    const { LRUCache } = require('lru-cache')
    const newCache = new LRUCache({ max: 500, ttl: 1000 * 60 * 10 })

    migrationData.forEach(item => {
      newCache.set(item.key, item.value)
      this.migrationLog.push({
        action: 'migrated',
        key: item.key,
        timestamp: Date.now()
      })
    })

    console.log(`Migration to lru-cache completed: ${migrationData.length} items`)
    return newCache
  }

  // Get migration log
  getMigrationLog() {
    return this.migrationLog
  }

  // Compatibility layer (recommended for temporary use only)
  createCompatibilityLayer(newCache) {
    return {
      put: (key, value, ttl) => {
        if (ttl) {
          return newCache.set(key, value, ttl)
        }
        return newCache.set(key, value)
      },
      get: (key) => {
        const value = newCache.get(key)
        return value !== undefined ? value : null
      },
      del: (key) => {
        return newCache.delete ? newCache.delete(key) : newCache.del(key)
      },
      clear: () => {
        return newCache.clear ? newCache.clear() : newCache.flushAll()
      },
      keys: () => {
        return newCache.keys ? newCache.keys() : []
      },
      size: () => {
        return newCache.size || newCache.getStats().keys
      }
    }
  }
}

// Migration example
const migrationHelper = new MigrationHelper()

// Prepare existing data
memoryCache.put('user:1', { name: 'John' })
memoryCache.put('config:app', { theme: 'dark' })

// Execute migration
const migrationData = migrationHelper.prepareMigration()
const newNodeCache = migrationHelper.convertToNodeCache(migrationData)

// Temporary use with compatibility layer
const compatCache = migrationHelper.createCompatibilityLayer(newNodeCache)

// Existing code works as-is
console.log(compatCache.get('user:1')) // { name: 'John' }
console.log(compatCache.size()) // 2

console.log('Migration log:', migrationHelper.getMigrationLog())

Enhanced Type Definitions for TypeScript

// Type-safe usage in TypeScript environment
declare module 'memory-cache' {
  interface CacheStatic {
    put<T>(key: string, value: T, time?: number): T
    get<T>(key: string): T | null
    del(key: string): boolean
    clear(): void
    size(): number
    keys(): string[]
  }
  
  const cache: CacheStatic
  export = cache
}

// Usage example
import cache = require('memory-cache')

interface User {
  id: number
  name: string
  email: string
}

interface CacheWrapper {
  setUser(userId: number, user: User): User
  getUser(userId: number): User | null
  deleteUser(userId: number): boolean
  getAllUsers(): User[]
}

class TypedMemoryCache implements CacheWrapper {
  setUser(userId: number, user: User): User {
    return cache.put<User>(`user:${userId}`, user, 30 * 60 * 1000) // 30 minutes
  }

  getUser(userId: number): User | null {
    return cache.get<User>(`user:${userId}`)
  }

  deleteUser(userId: number): boolean {
    return cache.del(`user:${userId}`)
  }

  getAllUsers(): User[] {
    const keys = cache.keys()
    return keys
      .filter(key => key.startsWith('user:'))
      .map(key => cache.get<User>(key))
      .filter((user): user is User => user !== null)
  }

  getStats() {
    return {
      totalKeys: cache.size(),
      keys: cache.keys()
    }
  }
}

// Usage example
const typedCache = new TypedMemoryCache()

const user: User = {
  id: 1,
  name: 'John Doe',
  email: '[email protected]'
}

typedCache.setUser(1, user)
const cachedUser = typedCache.getUser(1) // Type is User | null