@natiwo/cache
v0.1.0
Published
Redis caching with decorators and utilities
Readme
@natiwo/cache
Advanced caching with decorators and Redis
Installation
pnpm add @natiwo/cache ioredisFeatures
- 🎯 Method Decorators -
@Cacheable,@CacheInvalidate,@CachePut - ⚡ Redis Backend - Fast distributed caching
- 🔄 Auto-invalidation - Smart cache management
- ⏱️ TTL Support - Flexible expiration
- 🔧 Custom Key Generation - Full control over cache keys
Quick Start
Basic Caching
import { Cacheable, CacheInvalidate, getCacheManager } from '@natiwo/cache';
class UserService {
@Cacheable({ ttl: 600, prefix: 'user' })
async getUser(id: string) {
return await db.user.findUnique({ where: { id } });
}
@CacheInvalidate({
keys: (id: string) => [`user:getUser:${id}`]
})
async updateUser(id: string, data: UpdateUserInput) {
return await db.user.update({ where: { id }, data });
}
@CachePut({ ttl: 600, prefix: 'user' })
async createUser(data: CreateUserInput) {
return await db.user.create({ data });
}
}Manual Cache Operations
const cache = getCacheManager();
// Set with TTL
await cache.set('key', { data: 'value' }, { ttl: 300 });
// Get
const value = await cache.get('key');
// Delete
await cache.delete('key');
// Delete by pattern
await cache.deletePattern('user:*');
// Remember pattern
const user = await cache.remember(
`user:${id}`,
async () => await db.user.findUnique({ where: { id } }),
{ ttl: 600 }
);Decorators
@Cacheable
Caches method result automatically.
@Cacheable({
ttl?: number; // Time to live in seconds
prefix?: string; // Cache key prefix
keyGenerator?: (...args) => string; // Custom key function
condition?: (...args) => boolean; // Cache condition
})Example:
class ProductService {
@Cacheable({
ttl: 3600,
prefix: 'product',
condition: (id: string) => id !== 'admin'
})
async getProduct(id: string) {
return await db.product.findUnique({ where: { id } });
}
}@CacheInvalidate
Invalidates cache on method execution.
@CacheInvalidate({
keys?: string[] | ((...args) => string[]);
pattern?: string | ((...args) => string);
when?: 'before' | 'after'; // Default: 'after'
})Example:
class PostService {
@CacheInvalidate({
keys: (id: string) => [`post:${id}`, 'posts:all'],
when: 'after'
})
async deletePost(id: string) {
return await db.post.delete({ where: { id } });
}
@CacheInvalidate({
pattern: 'posts:*'
})
async createPost(data: CreatePostInput) {
return await db.post.create({ data });
}
}@CachePut
Always executes method and updates cache.
@CachePut({
ttl?: number;
prefix?: string;
keyGenerator?: (...args) => string;
})Example:
class OrderService {
@CachePut({ ttl: 1800, prefix: 'order' })
async processOrder(orderId: string) {
const order = await db.order.update({
where: { id: orderId },
data: { status: 'PROCESSED' },
});
return order;
}
}Advanced Usage
Custom Key Generator
class UserService {
@Cacheable({
ttl: 600,
keyGenerator: (email: string, includeProfile: boolean) => {
return `user:${email}:${includeProfile ? 'full' : 'basic'}`;
}
})
async getUserByEmail(email: string, includeProfile: boolean = false) {
return await db.user.findUnique({
where: { email },
include: { profile: includeProfile },
});
}
}Conditional Caching
@Cacheable({
ttl: 600,
condition: (userId: string, role: string) => {
// Don't cache for admins
return role !== 'admin';
}
})
async getPermissions(userId: string, role: string) {
return await db.permission.findMany({ where: { userId } });
}Multi-level Invalidation
@CacheInvalidate({
keys: (productId: string) => [
`product:${productId}`,
`product:${productId}:details`,
`product:${productId}:reviews`,
],
pattern: 'products:list:*',
})
async updateProduct(productId: string, data: UpdateProductInput) {
return await db.product.update({
where: { id: productId },
data,
});
}Configuration
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=your-password
REDIS_DB=0Or programmatic:
import { getCacheManager } from '@natiwo/cache';
import Redis from 'ioredis';
const redis = new Redis({
host: 'localhost',
port: 6379,
});
const cache = getCacheManager(redis);Best Practices
Use appropriate TTLs
// Frequently changing data - short TTL @Cacheable({ ttl: 60 }) async getActiveUsers() { ... } // Rarely changing data - longer TTL @Cacheable({ ttl: 3600 }) async getCountries() { ... }Invalidate strategically
// Invalidate related caches @CacheInvalidate({ keys: (userId) => [ `user:${userId}`, `user:${userId}:profile`, `user:${userId}:settings`, ] }) async updateUser(userId: string, data: any) { ... }Use patterns for bulk invalidation
@CacheInvalidate({ pattern: 'products:*' }) async importProducts(products: any[]) { ... }Monitor cache hit rates
const stats = await cache.info(); console.log(`Hit rate: ${stats.hitRate}%`);
License
MIT © NATIWO Sistemas
