| name | delon-cache-caching-strategies |
| description | Implement caching strategies using @delon/cache. Use this skill when adding memory cache, LocalStorage cache, SessionStorage cache, or cache interceptors for HTTP requests. Supports TTL-based expiration, cache invalidation, cache grouping, and persistent storage. Optimizes performance by reducing redundant API calls and database queries. |
| license | MIT |
@delon/cache Caching Strategies Skill
This skill helps implement caching using @delon/cache library.
Core Principles
Cache Types
- Memory Cache: Fast, in-memory caching (lost on page refresh)
- LocalStorage Cache: Persistent across sessions
- SessionStorage Cache: Persistent within session only
Features
- TTL-based expiration
- Cache invalidation (manual and automatic)
- Cache grouping and namespacing
- HTTP request caching with interceptors
- Observable support for async data
Configuration
import { ApplicationConfig } from '@angular/core';
import { provideDelonCache } from '@delon/cache';
export const appConfig: ApplicationConfig = {
providers: [
provideDelonCache({
mode: 'promise',
request_method: 'POST',
meta_key: '__cache_meta',
prefix: '',
expire: 3600000
})
]
};
Cache Service
import { Injectable, inject } from '@angular/core';
import { CacheService as DelonCacheService } from '@delon/cache';
import { Observable } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class CacheService {
private cache = inject(DelonCacheService);
set<T>(key: string, value: T, options?: {
type?: 'memory' | 'localStorage' | 'sessionStorage';
expire?: number;
}): void {
this.cache.set(key, value, {
type: options?.type || 'memory',
expire: options?.expire || 3600000
});
}
get<T>(key: string): T | null {
..<T>(key);
}
(: ): {
..(key);
}
(: ): {
..(key);
}
(): {
..();
}
getOrSet<T>(
: ,
: <T> | <T>,
?: {
?: | | ;
?: ;
}
): <T> {
(.(key)) {
( {
observer.(.<T>(key)!);
observer.();
});
}
result = ();
(result ) {
( {
result.({
: {
.(key, value, options);
observer.(value);
},
: observer.(err),
: observer.()
});
});
}
( {
result.( {
.(key, value, options);
observer.(value);
observer.();
}).( observer.(err));
});
}
}
Memory Cache (Default)
import { Component, inject, signal } from '@angular/core';
import { CacheService } from '@core/services/cache.service';
@Component({
selector: 'app-task-list',
template: `
<button nz-button (click)="loadTasks()">Load Tasks</button>
<button nz-button (click)="clearCache()">Clear Cache</button>
@if (loading()) {
<nz-spin />
} @else {
@for (task of tasks(); track task.id) {
<div>{{ task.title }}</div>
}
}
`
})
export class TaskListComponent {
private cacheService = inject(CacheService);
private taskService = inject(TaskService);
loading = signal(false);
tasks = signal<Task[]>([]);
private readonly CACHE_KEY = 'tasks:list';
async loadTasks(): Promise<void> {
const cached = this.cacheService.get<Task[]>(this.CACHE_KEY);
if (cached) {
console.log('Loading from cache');
..(cached);
;
}
..();
{
tasks = ..();
..(., tasks, {
: ,
: * *
});
..(tasks);
} {
..();
}
}
(): {
..(.);
.();
}
}
LocalStorage Cache (Persistent)
import { Injectable, inject } from '@angular/core';
import { CacheService } from '@core/services/cache.service';
@Injectable({ providedIn: 'root' })
export class UserPreferencesService {
private cacheService = inject(CacheService);
private readonly CACHE_KEY = 'user:preferences';
savePreferences(preferences: UserPreferences): void {
this.cacheService.set(this.CACHE_KEY, preferences, {
type: 'localStorage',
expire: 30 * 24 * 60 * 60 * 1000
});
}
loadPreferences(): UserPreferences | null {
return this.cacheService.get<UserPreferences>(this.);
}
(): {
..(.);
}
}
{
: | ;
: ;
: ;
}
SessionStorage Cache
@Injectable({ providedIn: 'root' })
export class SearchService {
private cacheService = inject(CacheService);
async search(query: string): Promise<SearchResult[]> {
const cacheKey = `search:${query}`;
const cached = this.cacheService.get<SearchResult[]>(cacheKey);
if (cached) {
return cached;
}
const results = await this.performSearch(query);
this.cacheService.set(cacheKey, results, {
type: 'sessionStorage',
expire: 30 * 60 * 1000
});
return results;
}
private async performSearch(query: string): <[]> {
[];
}
}
Lazy Loading with getOrSet
@Injectable({ providedIn: 'root' })
export class ConfigService {
private cacheService = inject(CacheService);
private http = inject(HttpClient);
loadConfig(): Observable<AppConfig> {
return this.cacheService.getOrSet(
'app:config',
() => this.http.get<AppConfig>('/api/config'),
{
type: 'localStorage',
expire: 24 * 60 * 60 * 1000
}
);
}
}
Cache Invalidation
Manual Invalidation
@Injectable({ providedIn: 'root' })
export class TaskService {
private cacheService = inject(CacheService);
private taskRepository = inject(TaskRepository);
async createTask(task: Omit<Task, 'id'>): Promise<Task> {
const created = await this.taskRepository.create(task);
this.cacheService.remove('tasks:list');
this.cacheService.remove(`tasks:blueprint:${task.blueprintId}`);
return created;
}
async updateTask(id: string, updates: Partial<Task>): Promise<Task> {
const updated = await this..(id, updates);
..();
..();
updated;
}
}
Group Cache Invalidation
@Injectable({ providedIn: 'root' })
export class CacheInvalidationService {
private cacheService = inject(CacheService);
invalidateGroup(prefix: string): void {
const keys = this.getCacheKeys(prefix);
keys.forEach(key => this.cacheService.remove(key));
}
private cacheKeys = new Set<string>();
registerCacheKey(key: string): void {
this.cacheKeys.add(key);
}
private getCacheKeys(prefix: string): string[] {
return Array.from(this.cacheKeys).filter(key => key.startsWith(prefix));
}
}
HTTP Cache Interceptor
import { HttpInterceptorFn, HttpResponse } from '@angular/common/http';
import { inject } from '@angular/core';
import { CacheService } from '@core/services/cache.service';
import { of, tap } from 'rxjs';
export const cacheInterceptor: HttpInterceptorFn = (req, next) => {
const cacheService = inject(CacheService);
if (req.method !== 'GET') {
return next(req);
}
const skipCache = req.headers.has('X-Skip-Cache') ||
req.url.includes('/api/realtime');
if (skipCache) {
return next(req);
}
const cacheKey = `http:${req.urlWithParams}`;
cached = cacheService.<<>>(cacheKey);
(cached) {
.(, cacheKey);
(cached);
}
(req).(
( {
(event ) {
cacheService.(cacheKey, event, {
: ,
: * *
});
}
})
);
};
Register Interceptor
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { cacheInterceptor } from '@core/interceptors/cache.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([cacheInterceptor])
)
]
};
Cache Namespacing
@Injectable({ providedIn: 'root' })
export class NamespacedCacheService {
private cacheService = inject(CacheService);
constructor(private namespace: string) {}
private getKey(key: string): string {
return `${this.namespace}:${key}`;
}
set<T>(key: string, value: T, options?: any): void {
this.cacheService.set(this.getKey(key), value, options);
}
get<T>(key: string): T | null {
return this.cacheService.get<T>(this.getKey(key));
}
remove(key: string): void {
this.cacheService.remove(this.(key));
}
}
({ : })
{
() {
();
}
}
Cache Patterns
Read-Through Cache
async getTask(id: string): Promise<Task> {
const cacheKey = `tasks:${id}`;
let task = this.cacheService.get<Task>(cacheKey);
if (task) {
return task;
}
task = await this.taskRepository.findById(id);
if (task) {
this.cacheService.set(cacheKey, task, {
type: 'memory',
expire: 10 * 60 * 1000
});
}
return task;
}
Write-Through Cache
async updateTask(id: string, updates: Partial<Task>): Promise<Task> {
const updated = await this.taskRepository.update(id, updates);
const cacheKey = `tasks:${id}`;
this.cacheService.set(cacheKey, updated, {
type: 'memory',
expire: 10 * 60 * 1000
});
return updated;
}
Cache-Aside Pattern
async getTasks(blueprintId: string): Promise<Task[]> {
const cacheKey = `tasks:blueprint:${blueprintId}`;
if (this.cacheService.has(cacheKey)) {
return this.cacheService.get<Task[]>(cacheKey)!;
}
const tasks = await this.taskRepository.findByBlueprintId(blueprintId);
this.cacheService.set(cacheKey, tasks, {
type: 'memory',
expire: 5 * 60 * 1000
});
return tasks;
}
Best Practices
Cache Key Naming
'users:123'
'tasks:blueprint:abc-123'
'config:app:theme'
'user'
'data123'
'cache'
TTL Guidelines
this.cacheService.set('config', data, { expire: 24 * 60 * 60 * 1000 });
this.cacheService.set('tasks', data, { expire: 5 * 60 * 1000 });
this.cacheService.set('user:prefs', data, { type: 'sessionStorage' });
this.cacheService.set('settings', data, {
type: 'localStorage',
expire: 30 * 24 * 60 * 60 * 1000
});
Checklist
When implementing caching:
References