Caching
Learn how to use the Store for high-performance caching. Store and retrieve data, manage TTL, check key existence, and implement cache patterns.
Overview
Caching is one of the primary use cases for the Store adapter. It allows you to store frequently accessed data in Redis, dramatically improving response times and reducing load on your database or external APIs. Effective caching can transform slow, database-heavy applications into fast, responsive systems that scale effortlessly.
The Store adapter automatically handles JSON serialization and deserialization, so you can cache complex objects without manual conversion. This makes it easy to cache API responses, database query results, computed values, and any data that's expensive to generate but doesn't change frequently.
Basic Operations
The Store adapter provides four essential operations for caching: storing values, retrieving values, checking key existence, and deleting keys. Understanding these operations forms the foundation of effective caching strategies. Each operation serves a specific purpose in managing your cache effectively.
These basic operations are simple but powerful—they enable you to implement sophisticated caching patterns without complex infrastructure. The adapter handles all the serialization and connection management, so you can focus on what to cache and when.
Cache Patterns
Understanding different caching patterns helps you choose the right approach for your use case. Each pattern has trade-offs between consistency, performance, and complexity. The most common patterns are cache-aside, write-through, and write-behind—each serves different needs in modern applications.
Cache patterns determine when data is written to and read from the cache relative to the source of truth (like your database). Choosing the right pattern ensures your cache improves performance without introducing data consistency issues.
TTL Management
Time-to-live (TTL) management is crucial for maintaining fresh cache data. The Store adapter makes it easy to set expiration times when storing values and to update TTL on existing keys. Understanding TTL management helps you balance cache freshness with performance.
TTL ensures your cache doesn't serve stale data indefinitely. Setting appropriate TTL values based on how often your data changes ensures your cache improves performance without sacrificing data accuracy.
Setting TTL on Set
Set expiration time when storing values:
// Cache for 1 hour
await igniter.store.set('key', value, { ttl: 3600 });
// Cache for 30 minutes
await igniter.store.set('key', value, { ttl: 1800 });
// Cache for 1 day
await igniter.store.set('key', value, { ttl: 86400 });Updating TTL
Use expire() to update or set TTL on existing keys:
// Set TTL on existing key
await igniter.store.set('user:123', userData);
await igniter.store.expire('user:123', 3600); // Extend to 1 hour
// Refresh TTL after access
const user = await igniter.store.get<User>('user:123');
if (user) {
await igniter.store.expire('user:123', 3600); // Refresh expiration
}Key Naming Conventions
Use consistent naming patterns for better organization:
// Entity patterns
`user:${userId}` // Single user
`users:list` // List of users
`users:list:page:${pageNumber}` // Paginated list
// Feature-specific patterns
`session:${sessionId}` // User session
`rate:limit:${userId}` // Rate limiting
`api:response:${endpoint}` // API response cache
// Scoped patterns
`tenant:${tenantId}:user:${userId}` // Multi-tenant
`env:${env}:key:${key}` // Environment-specificBest Practice
Use colons (:) to create hierarchical key names. This makes it easier to identify and manage related keys.
Advanced Caching Strategies
Advanced caching strategies help you optimize cache performance and efficiency. These strategies address common challenges like cache warming, invalidation, and conditional caching. Understanding these patterns helps you build production-ready caching systems that scale well.
These strategies go beyond basic caching operations to address real-world challenges like cold starts, cache consistency, and performance optimization. They're essential for building high-performance applications that rely heavily on caching.
Data Serialization
The Store adapter automatically handles JSON serialization and deserialization for complex objects. This means you can cache nested objects, arrays, and complex data structures without manual conversion. The adapter handles all the serialization details, making caching straightforward.
Understanding serialization helps you cache complex data structures effectively. While most JavaScript types serialize well, some types like Date, Map, and Set require special handling.
The Store adapter automatically handles JSON serialization:
// Complex objects are automatically serialized
const complexData = {
user: {
id: '123',
name: 'John',
metadata: {
preferences: ['theme-dark', 'notifications-on'],
lastLogin: new Date(),
},
},
nested: {
array: [1, 2, 3],
map: new Map([['key', 'value']]),
},
};
// Stored as JSON string in Redis
await igniter.store.set('complex:data', complexData, { ttl: 3600 });
// Retrieved and automatically parsed
const retrieved = await igniter.store.get<typeof complexData>('complex:data');Limitations
Some JavaScript types (like Date, Map, Set) need special handling. Consider serializing them manually or using a library like superjson for complex types.
Error Handling
Cache errors shouldn't break your application. Handle cache errors gracefully by falling back to the source of truth (like your database) when cache operations fail. This ensures your application remains functional even when Redis is temporarily unavailable.
Good error handling prevents cache failures from affecting user experience. Always have fallback strategies that allow your application to continue functioning when cache operations fail.
Handle cache errors gracefully:
const getCachedUser = async (id: string, context: AppContext) => {
try {
const cached = await igniter.store.get<User>(`user:${id}`);
if (cached) {
return cached;
}
} catch (error) {
// Log error but don't fail - fall back to database
context.logger.warn('Cache read failed', { error, key: `user:${id}` });
}
// Fallback to database
return await context.db.user.findUnique({ where: { id } });
};Performance Tips
Optimizing cache performance involves choosing appropriate TTL values, using batch operations efficiently, and managing memory carefully. These tips help you get the most out of your cache while avoiding common performance pitfalls.
Good cache performance comes from understanding your data access patterns and optimizing accordingly. These tips address common performance concerns and help you build efficient caching systems.
Real-World Examples
These real-world examples demonstrate practical caching patterns you can use in production applications. They show how to cache API responses, manage user sessions, and implement common caching use cases effectively.
Real-world examples help you understand how caching fits into actual applications. These patterns address common scenarios and provide production-ready solutions you can adapt for your own use cases.
Installation
Install and configure the Redis adapter for Store. Set up Redis connection, create the adapter instance, and register it with Igniter.js.
Pub/Sub Messaging
Publish and subscribe to channels for event-driven communication. Build real-time features, microservices communication, and decoupled architectures using Pub/Sub.