We use cookies to enhance your experience on the site
CodeWorlds

HTTP Patterns in Practice

CRUD Service with Full Support

1@Injectable({ providedIn: 'root' })
2export class ApiService<T extends { id: number }> {
3  constructor(
4    private http: HttpClient,
5    private baseUrl: string
6  ) {}
7
8  getAll(params?: Record<string, string>): Observable<T[]> {
9    return this.http.get<T[]>(this.baseUrl, {
10      params: params ? new HttpParams({ fromObject: params }) : undefined
11    });
12  }
13
14  getById(id: number): Observable<T> {
15    return this.http.get<T>(\`\${this.baseUrl}/\${id}\`);
16  }
17
18  create(item: Omit<T, 'id'>): Observable<T> {
19    return this.http.post<T>(this.baseUrl, item);
20  }
21
22  update(item: T): Observable<T> {
23    return this.http.put<T>(\`\${this.baseUrl}/\${item.id}\`, item);
24  }
25
26  delete(id: number): Observable<void> {
27    return this.http.delete<void>(\`\${this.baseUrl}/\${id}\`);
28  }
29}
30
31// Usage
32@Injectable({ providedIn: 'root' })
33export class SamuraiApiService extends ApiService<Samurai> {
34  constructor(http: HttpClient) {
35    super(http, '/api/samurai');
36  }
37}

HTTP with Signals (Angular 17+)

1@Component({
2  template: \`
3    @if (loading()) {
4      <app-spinner />
5    } @else if (error()) {
6      <div class="error">{{ error() }}</div>
7    } @else {
8      @for (samurai of samuraiList(); track samurai.id) {
9        <app-samurai-card [samurai]="samurai" />
10      }
11    }
12  \`
13})
14export class SamuraiListComponent {
15  private samuraiService = inject(SamuraiService);
16
17  // State as signals
18  samuraiList = signal<Samurai[]>([]);
19  loading = signal(true);
20  error = signal<string | null>(null);
21
22  constructor() {
23    this.loadSamurai();
24  }
25
26  loadSamurai(): void {
27    this.loading.set(true);
28    this.error.set(null);
29
30    this.samuraiService.getAll().pipe(
31      takeUntilDestroyed()
32    ).subscribe({
33      next: (samurai) => {
34        this.samuraiList.set(samurai);
35        this.loading.set(false);
36      },
37      error: (err) => {
38        this.error.set(err.message);
39        this.loading.set(false);
40      }
41    });
42  }
43}

Resource API (Angular 19)

1import { resource } from '@angular/core';
2
3@Component({
4  template: \`
5    @switch (samuraiResource.status()) {
6      @case ('loading') {
7        <app-spinner />
8      }
9      @case ('error') {
10        <div class="error">{{ samuraiResource.error() }}</div>
11      }
12      @case ('success') {
13        <app-samurai-detail [samurai]="samuraiResource.value()!" />
14      }
15    }
16  \`
17})
18export class SamuraiDetailComponent {
19  id = input.required<number>();
20
21  samuraiResource = resource({
22    request: () => this.id(),
23    loader: async ({ request: id }) => {
24      const response = await fetch(\`/api/samurai/\${id}\`);
25      if (!response.ok) throw new Error('Failed to load');
26      return response.json() as Promise<Samurai>;
27    }
28  });
29
30  refresh(): void {
31    this.samuraiResource.reload();
32  }
33}
Go to CodeWorlds