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}