Skip to content
delon-cache-caching-strategies logo

@delon/cache Caching Strategies Skill

delon-cache-caching-strategies

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...

aiskillstore/marketplace0installs430stars

SKILL.md

Full skill instructions

@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

// src/​app/​app.config.ts
import { ApplicationConfig } from '@angular/​core';
import { provideDelonCache } from '@delon/​cache';

export const appConfig: ApplicationConfig = {
  providers: [
    provideDelonCache({
      mode: 'promise',  // 'promise' | 'none'
      request_method: 'POST',
      meta_key: '__cache_meta',
      prefix: '',
      expire: 3600000  // Default TTL: 1 hour (ms)
    })
  ]
};

Cache Service

// src/​app/​core/​services/​cache.service.ts
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 cache with key
   */
  set<T>(key: string, value: T, options?: { 
    type?: 'memory' | 'localStorage' | 'sessionStorage';
    expire?: number; // TTL in milliseconds
  }): void {
    this.cache.set(key, value, {
      type: options?.type || 'memory',
      expire: options?.expire || 3600000 // 1 hour default
    });
  }
  
  /​**
   * Get cache by key
   */
  get<T>(key: string): T | null {
    return this.cache.get<T>(key);
  }
  
  /​**
   * Check if cache exists and is not expired
   */
  has(key: string): boolean {
    return this.cache.has(key);
  }
  
  /​**
   * Remove cache by key
   */
  remove(key: string): void {
    this.cache.remove(key);
  }
  
  /​**
   * Clear all cache
   */
  clear(): void {
    this.cache.clear();
  }
  
  /​**
   * Get or set cache (lazy loading pattern)
   */
  getOrSet<T>(
    key: string, 
    factory: () => Observable<T> | Promise<T>,
    options?: {
      type?: 'memory' | 'localStorage' | 'sessionStorage';
      expire?: number;
    }
  ): Observable<T> {
    if (this.has(key)) {
      return new Observable(observer => {
        observer.next(this.get<T>(key)!);
        observer.complete();
      });
    }
    
    const result = factory();
    
    if (result instanceof Observable) {
      return new Observable(observer => {
        result.subscribe({
          next: (value) => {
            this.set(key, value, options);
            observer.next(value);
          },
          error: (err) => observer.error(err),
          complete: () => observer.complete()
        });
      });
    }
    
    return new Observable(observer => {
      result.then(value => {
        this.set(key, value, options);
        observer.next(value);
        observer.complete();
      }).catch(err => observer.error(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> {
    // Try to get from cache first
    const cached = this.cacheService.get<Task[]>(this.CACHE_KEY);
    
    if (cached) {
      console.log('Loading from cache');
      this.tasks.set(cached);
      return;
    }
    
    // Load from API
    this.loading.set(true);
    try {
      const tasks = await this.taskService.getTasks();
      
      // Cache for 5 minutes
      this.cacheService.set(this.CACHE_KEY, tasks, {
        type: 'memory',
        expire: 5 * 60 * 1000 // 5 minutes
      });
      
      this.tasks.set(tasks);
    } finally {
      this.loading.set(false);
    }
  }
  
  clearCache(): void {
    this.cacheService.remove(this.CACHE_KEY);
    console.log('Cache cleared');
  }
}

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';
  
  /​**
   * Save user preferences (persists across sessions)
   */
  savePreferences(preferences: UserPreferences): void {
    this.cacheService.set(this.CACHE_KEY, preferences, {
      type: 'localStorage',
      expire: 30 * 24 * 60 * 60 * 1000 // 30 days
    });
  }
  
  /​**
   * Load user preferences
   */
  loadPreferences(): UserPreferences | null {
    return this.cacheService.get<UserPreferences>(this.CACHE_KEY);
  }
  
  /​**
   * Clear preferences
   */
  clearPreferences(): void {
    this.cacheService.remove(this.CACHE_KEY);
  }
}

interface UserPreferences {
  theme: 'light' | 'dark';
  language: string;
  sidebarCollapsed: boolean;
}

SessionStorage Cache

/​**
 * Cache search results for current session only
 */
@Injectable({ providedIn: 'root' })
export class SearchService {
  private cacheService = inject(CacheService);
  
  async search(query: string): Promise<SearchResult[]> {
    const cacheKey = `search:${query}`;
    
    // Check session cache
    const cached = this.cacheService.get<SearchResult[]>(cacheKey);
    if (cached) {
      return cached;
    }
    
    // Perform search
    const results = await this.performSearch(query);
    
    // Cache for current session only
    this.cacheService.set(cacheKey, results, {
      type: 'sessionStorage',
      expire: 30 * 60 * 1000 // 30 minutes
    });
    
    return results;
  }
  
  private async performSearch(query: string): Promise<SearchResult[]> {
    // API call
    return [];
  }
}

Lazy Loading with getOrSet

@Injectable({ providedIn: 'root' })
export class ConfigService {
  private cacheService = inject(CacheService);
  private http = inject(HttpClient);
  
  /​**
   * Load configuration with automatic caching
   */
  loadConfig(): Observable<AppConfig> {
    return this.cacheService.getOrSet(
      'app:config',
      () => this.http.get<AppConfig>('/​api/​config'),
      {
        type: 'localStorage',
        expire: 24 * 60 * 60 * 1000 // 24 hours
      }
    );
  }
}

Cache Invalidation

Manual Invalidation

@Injectable({ providedIn: 'root' })
export class TaskService {
  private cacheService = inject(CacheService);
  private taskRepository = inject(TaskRepository);
  
  /​**
   * Create task and invalidate cache
   */
  async createTask(task: Omit<Task, 'id'>): Promise<Task> {
    const created = await this.taskRepository.create(task);
    
    // Invalidate task list cache
    this.cacheService.remove('tasks:list');
    this.cacheService.remove(`tasks:blueprint:${task.blueprintId}`);
    
    return created;
  }
  
  /​**
   * Update task and invalidate cache
   */
  async updateTask(id: string, updates: Partial<Task>): Promise<Task> {
    const updated = await this.taskRepository.update(id, updates);
    
    // Invalidate specific task cache
    this.cacheService.remove(`tasks:${id}`);
    
    // Invalidate list caches
    this.cacheService.remove('tasks:list');
    
    return updated;
  }
}

Group Cache Invalidation

@Injectable({ providedIn: 'root' })
export class CacheInvalidationService {
  private cacheService = inject(CacheService);
  
  /​**
   * Invalidate all caches with prefix
   */
  invalidateGroup(prefix: string): void {
    // @delon/​cache doesn't have built-in group invalidation
    // So we track cache keys manually
    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

// src/​app/​core/​interceptors/​cache.interceptor.ts
import { HttpInterceptorFn, HttpResponse } from '@angular/​common/​http';
import { inject } from '@angular/​core';
import { CacheService } from '@core/​services/​cache.service';
import { of, tap } from 'rxjs';

/​**
 * Cache GET requests
 */
export const cacheInterceptor: HttpInterceptorFn = (req, next) => {
  const cacheService = inject(CacheService);
  
  // Only cache GET requests
  if (req.method !== 'GET') {
    return next(req);
  }
  
  // Skip cache for certain URLs
  const skipCache = req.headers.has('X-Skip-Cache') || 
                    req.url.includes('/​api/​realtime');
  
  if (skipCache) {
    return next(req);
  }
  
  // Generate cache key from URL + params
  const cacheKey = `http:${req.urlWithParams}`;
  
  // Check cache
  const cached = cacheService.get<HttpResponse<any>>(cacheKey);
  if (cached) {
    console.log('Serving from cache:', cacheKey);
    return of(cached);
  }
  
  // Make request and cache response
  return next(req).pipe(
    tap(event => {
      if (event instanceof HttpResponse) {
        cacheService.set(cacheKey, event, {
          type: 'memory',
          expire: 5 * 60 * 1000 // 5 minutes
        });
      }
    })
  );
};

Register Interceptor

// src/​app/​app.config.ts
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.getKey(key));
  }
}

// Usage
@Injectable({ providedIn: 'root' })
export class TaskCacheService extends NamespacedCacheService {
  constructor() {
    super('tasks');
  }
}

Cache Patterns

Read-Through Cache

async getTask(id: string): Promise<Task> {
  const cacheKey = `tasks:${id}`;
  
  // Try cache
  let task = this.cacheService.get<Task>(cacheKey);
  if (task) {
    return task;
  }
  
  // Load from repository
  task = await this.taskRepository.findById(id);
  
  // Cache result
  if (task) {
    this.cacheService.set(cacheKey, task, {
      type: 'memory',
      expire: 10 * 60 * 1000 // 10 minutes
    });
  }
  
  return task;
}

Write-Through Cache

async updateTask(id: string, updates: Partial<Task>): Promise<Task> {
  // Update in repository
  const updated = await this.taskRepository.update(id, updates);
  
  // Update cache
  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}`;
  
  // Check cache
  if (this.cacheService.has(cacheKey)) {
    return this.cacheService.get<Task[]>(cacheKey)!;
  }
  
  // Load from repository
  const tasks = await this.taskRepository.findByBlueprintId(blueprintId);
  
  // Populate cache
  this.cacheService.set(cacheKey, tasks, {
    type: 'memory',
    expire: 5 * 60 * 1000
  });
  
  return tasks;
}

Best Practices

Cache Key Naming

// Good: Descriptive, hierarchical keys
'users:123'
'tasks:blueprint:abc-123'
'config:app:theme'

// Bad: Generic, flat keys
'user'
'data123'
'cache'

TTL Guidelines

// Static data: 1 hour - 1 day
this.cacheService.set('config', data, { expire: 24 * 60 * 60 * 1000 });

// Dynamic data: 1-10 minutes
this.cacheService.set('tasks', data, { expire: 5 * 60 * 1000 });

// User-specific: Session duration
this.cacheService.set('user:prefs', data, { type: 'sessionStorage' });

// Persistent: 30 days
this.cacheService.set('settings', data, { 
  type: 'localStorage',
  expire: 30 * 24 * 60 * 60 * 1000 
});

Checklist

When implementing caching:

  • Choose appropriate cache type (memory/​localStorage/​sessionStorage)
  • Set reasonable TTL values
  • Implement cache invalidation on data changes
  • Use descriptive, hierarchical cache keys
  • Handle cache misses gracefully
  • Monitor cache hit/​miss rates
  • Consider cache size limits
  • Test cache expiration
  • Document caching strategy
  • Implement cache warming for critical data

References

More skills from aiskillstore

docx logo
aiskillstore/marketplace

docx

Comprehensive document creation, editing, and analysis with support for tracked changes, comments, formatting preservation, and text extraction. When Claude needs to work with professional documents (.docx files) for: (1) Creating new documents, (2) Modifying or editing content, (3) Working with tracked changes, (4) Adding comments, or any other document tasks

430 216
View
agent-development logo
aiskillstore/marketplace

agent-development

This handles everything around building autonomous agents for Claude Code plugins.

430 197
View
doc-coauthoring logo
aiskillstore/marketplace

doc-coauthoring

Guide users through a structured workflow for co-authoring documentation. Use when user wants to write documentation, proposals, technical specs, decision docs, or similar structured content. This workflow helps users efficiently transfer context, refine content through iteration, and verify the doc works for readers. Trigger when user mentions writing docs, creating proposals, drafting specs, or similar documentation tasks.

430 196
View
skill-development logo
aiskillstore/marketplace

skill-development

This is the meta-skill for building skills themselves.

430 196
View
canvas-design logo
aiskillstore/marketplace

canvas-design

Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.

430 193
View
webapp-testing logo
aiskillstore/marketplace

webapp-testing

Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.

430 191
View
hook-development logo
aiskillstore/marketplace

hook-development

Helps you build event-driven automation for Claude Code using hooks that trigger on tool use, session events, and stop conditions.

430 187
View
algorithmic-art logo
aiskillstore/marketplace

algorithmic-art

Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.

430 185
View
command-development logo
aiskillstore/marketplace

command-development

Shows Claude Code how to build slash commands from markdown files, covering the YAML frontmatter options, dynamic arguments with $1/$2...

430 185
View
plugin-structure logo
aiskillstore/marketplace

plugin-structure

This is your reference when scaffolding Claude Code plugins or figuring out why components aren't loading.

430 183
View
skill-installer logo
aiskillstore/marketplace

skill-installer

Install Codex skills into $CODEX_HOME/skills from a curated list or a GitHub repo path. Use when a user asks to list installable skills, install a curated skill, or install a skill from another repo (including private repos).

430 180
View
web-artifacts-builder logo
aiskillstore/marketplace

web-artifacts-builder

Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.

430 177
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View