Simple and extensible caching module supporting decorators
npm install @node-ts-cache/core


Simple and extensible caching module for TypeScript/Node.js with decorator support.
``bash`
npm install @node-ts-cache/core
`typescript
import { Cache, ExpirationStrategy, MemoryStorage } from '@node-ts-cache/core';
const cacheStrategy = new ExpirationStrategy(new MemoryStorage());
class UserService {
@Cache(cacheStrategy, { ttl: 60 })
async getUser(id: string): Promise
return await database.findUser(id);
}
}
`
The core package includes MemoryStorage and FsJsonStorage. Additional storage backends are available as separate packages:
| Package | Storage Type | Sync/Async | Use Case |
| -------------------------------------- | --------------------------------------------------------------------- | ---------- | -------------------------------------- |
| @node-ts-cache/core | MemoryStorage | Sync | Development, simple caching |@node-ts-cache/core
| | FsJsonStorage | Async | Persistent local cache |@node-ts-cache/node-cache-storage
| | node-cache | Sync | Production single-instance with TTL |@node-ts-cache/lru-storage
| | lru-cache | Sync | Memory-bounded with automatic eviction |@node-ts-cache/redis-storage
| | redis (v4.x) | Async | Shared cache |@node-ts-cache/ioredis-storage
| | ioredis | Async | Shared cache with compression |@node-ts-cache/lru-redis-storage
| | LRU + Redis | Async | Two-tier: fast local + shared remote |@node-ts-cache/elasticsearch-storage
| | elasticsearch | Async | Search-integrated caching |@node-ts-cache/memcached-storage
| | memcached | Async | High-performance distributed cache |@node-ts-cache/valkey-storage
| | iovalkey | Async | Redis-compatible, open source |
Caches async method results. Cache key is generated from class name, method name, and arguments.
`typescript`
class ProductService {
@Cache(strategy, { ttl: 300 })
async getProduct(id: string): Promise
return await db.products.findById(id);
}
}
Note: @Cache always returns a Promise since cache operations may be asynchronous.
Caches synchronous method results without converting to Promises. Use with synchronous storages like MemoryStorage or LRUStorage.
`typescript`
class ConfigService {
@SyncCache(strategy, { ttl: 60 })
getConfig(key: string): ConfigValue {
return computeConfig(key);
}
}
Multi-tier caching with batch operations for array-based lookups.
`typescriptuser:${id}
class UserService {
@MultiCache([localCache, redisCache], 0, id => , { ttl: 300 })`
async getUsersByIds(userIds: string[]): Promise
return await db.users.findByIds(userIds);
}
}
Use the caching strategy directly without decorators:
`typescript
const cache = new ExpirationStrategy(new MemoryStorage());
// Get item
const value = await cache.getItem('key');
// Set item with TTL
await cache.setItem('key', data, { ttl: 300 });
// Delete item
await cache.setItem('key', undefined);
// Clear all
await cache.clear();
`
| Option | Type | Default | Description |
| ----------------- | --------- | ------- | --------------------------------------------------------------------------------- |
| ttl | number | 60 | Time to live in seconds |isLazy
| | boolean | true | If true, delete on access after expiration. If false, delete via setTimeout |isCachedForever
| | boolean | false | If true, items never expire |
`typescript
// Cache for 5 minutes with lazy expiration
await strategy.setItem('key', value, { ttl: 300, isLazy: true });
// Cache forever
await strategy.setItem('key', value, { isCachedForever: true });
// Cache with eager expiration (auto-delete after TTL)
await strategy.setItem('key', value, { ttl: 10, isLazy: false });
`
Override default key generation by implementing ISyncKeyStrategy or IAsyncKeyStrategy:
`typescript${className}::${methodName}::${args.join('-')}
class CustomKeyStrategy implements ISyncKeyStrategy {
getKey(className: string, methodName: string, args: any[]): string | undefined {
if (args[0] === 'skip') return undefined; // Skip caching
return ;
}
}
class MyService {
@Cache(strategy, { ttl: 60 }, new CustomKeyStrategy())
async getData(id: string): Promise {
return fetchData(id);
}
}
`
Concurrent calls with the same cache key share the same pending promise:
`typescript`
// All three calls share one database request
const [a, b, c] = await Promise.all([
service.fetchData('123'),
service.fetchData('123'),
service.fetchData('123')
]);
- undefined: Cache miss or skip cachingnull
- : Cached value (e.g., "not found" result)
`typescript`
async findUser(id: string): Promise
const user = await db.findUser(id);
return user ?? null; // Cache "not found" as null
}
| Variable | Description |
| ------------------------- | --------------------------------------------------- |
| DISABLE_CACHE_DECORATOR | Set to any value to disable all @Cache` decorators |
See ADVANCED.md for:
- Interface definitions for implementing custom storages
- Detailed storage configuration examples
- @MultiCache in-depth usage
- Error handling patterns
MIT License