We use cookies to enhance your experience on the site
CodeWorlds

Error Handling and Caching

Comprehensive Error Handling

1import { catchError, retry, shareReplay } from 'rxjs/operators';
2import { throwError, of } from 'rxjs';
3
4@Injectable({ providedIn: 'root' })
5export class SamuraiService {
6  private http = inject(HttpClient);
7
8  getSamurai(id: number): Observable<Samurai> {
9    return this.http.get<Samurai>(\`/api/samurai/\${id}\`).pipe(
10      retry({
11        count: 3,
12        delay: 1000
13      }),
14      catchError(this.handleError)
15    );
16  }
17
18  private handleError(error: HttpErrorResponse): Observable<never> {
19    let errorMessage = 'Unknown error!';
20
21    if (error.status === 0) {
22      // Network error
23      errorMessage = 'No connection to the server';
24    } else if (error.status === 404) {
25      errorMessage = 'Samurai not found';
26    } else if (error.status === 403) {
27      errorMessage = 'Access denied';
28    } else if (error.status >= 500) {
29      errorMessage = 'Server error - try again later';
30    } else if (error.error?.message) {
31      errorMessage = error.error.message;
32    }
33
34    console.error('HTTP Error:', error);
35    return throwError(() => new Error(errorMessage));
36  }
37}

Simple Cache with shareReplay

1@Injectable({ providedIn: 'root' })
2export class CachedSamuraiService {
3  private http = inject(HttpClient);
4  private cache$ = new Map<string, Observable<any>>();
5
6  getSamurai(id: number): Observable<Samurai> {
7    const key = \`samurai-\${id}\`;
8
9    if (!this.cache$.has(key)) {
10      this.cache$.set(key,
11        this.http.get<Samurai>(\`/api/samurai/\${id}\`).pipe(
12          shareReplay({ bufferSize: 1, refCount: true })
13        )
14      );
15    }
16
17    return this.cache$.get(key)!;
18  }
19
20  invalidate(id?: number): void {
21    if (id) {
22      this.cache$.delete(\`samurai-\${id}\`);
23    } else {
24      this.cache$.clear();
25    }
26  }
27}

Cache with Expiration Time

1interface CacheEntry<T> {
2  data$: Observable<T>;
3  expiry: number;
4}
5
6@Injectable({ providedIn: 'root' })
7export class TimedCacheService {
8  private cache = new Map<string, CacheEntry<any>>();
9  private readonly TTL = 5 * 60 * 1000;  // 5 minutes
10
11  get<T>(url: string): Observable<T> {
12    const cached = this.cache.get(url);
13
14    if (cached && cached.expiry > Date.now()) {
15      return cached.data$;
16    }
17
18    const data$ = this.http.get<T>(url).pipe(
19      shareReplay({ bufferSize: 1, refCount: true })
20    );
21
22    this.cache.set(url, {
23      data$,
24      expiry: Date.now() + this.TTL
25    });
26
27    return data$;
28  }
29}
Go to CodeWorlds