Fortgeschritten262025-01-31

Angular HTTP Client & APIs - Backend-Kommunikation

Lerne HTTP Requests mit Angular HttpClient: GET, POST, PUT, DELETE, Error Handling, Interceptors und RxJS Operators für professionelle API-Integration.

#angular#http#api#httpclient#rxjs#rest#interceptors

Angular HTTP Client & APIs

Zeit für echte Daten! Lerne mit Backend-APIs zu kommunizieren. 🌐

📋 In diesem Artikel lernst du:

  • HttpClient Setup und Konfiguration
  • GET Requests (Daten laden)
  • POST Requests (Daten erstellen)
  • PUT/PATCH Requests (Daten updaten)
  • DELETE Requests (Daten löschen)
  • Error Handling
  • HTTP Headers und Query Parameters
  • Interceptors (Request/Response Manipulation)
  • RxJS Operators für HTTP
  • Loading States und Retry Logic

Was ist HttpClient?

HttpClient ist Angular's Service für HTTP-Kommunikation mit Backend-APIs.

Features:

  • ✅ Type-safe (TypeScript Interfaces)
  • ✅ Observable-basiert (RxJS)
  • ✅ Automatisches JSON Parsing
  • ✅ Error Handling
  • ✅ Interceptors (Middleware)
  • ✅ Testing Support

HttpClient Setup

HttpClientModule importieren

app.module.ts:

import { HttpClientModule } from '@angular/common/http';

@NgModule({
  imports: [
    BrowserModule,
    HttpClientModule  // ✅ Wichtig!
  ],
  // ...
})
export class AppModule { }

Ohne diesen Import funktioniert HttpClient NICHT!

GET Requests - Daten laden

Basic GET Request

API Endpoint: https://jsonplaceholder.typicode.com/users

user.service.ts:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

export interface User {
  id: number;
  name: string;
  email: string;
  username: string;
}

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private apiUrl = 'https://jsonplaceholder.typicode.com/users';

  constructor(private http: HttpClient) {}

  // ✅ GET Request
  getUsers(): Observable<User[]> {
    return this.http.get<User[]>(this.apiUrl);
  }

  // ✅ GET Single User
  getUserById(id: number): Observable<User> {
    return this.http.get<User>(`${this.apiUrl}/${id}`);
  }
}

Erklärung:

  • http.get<User[]>() - Type-safe! Response ist User[]
  • Return Type: Observable<User[]> - Asynchron!
  • .get() führt Request NICHT aus! Erst bei .subscribe()

Service in Komponente nutzen

user-list.component.ts:

import { Component, OnInit } from '@angular/core';
import { UserService, User } from './services/user.service';

@Component({
  selector: 'app-user-list',
  template: `
    <div class="user-list">
      <h1>Users</h1>

      <!-- Loading State -->
      <div *ngIf="loading" class="loading">
        Lädt Daten...
      </div>

      <!-- Error State -->
      <div *ngIf="error" class="error">
        {{ error }}
      </div>

      <!-- Success State -->
      <div *ngIf="!loading && !error">
        <div *ngFor="let user of users" class="user-card">
          <h3>{{ user.name }}</h3>
          <p>{{ user.email }}</p>
          <p>@{{ user.username }}</p>
        </div>
      </div>
    </div>
  `,
  styles: [`
    .user-card {
      padding: 1rem;
      border: 1px solid #e5e7eb;
      border-radius: 8px;
      margin-bottom: 1rem;
    }
    .loading {
      text-align: center;
      padding: 2rem;
      color: #6b7280;
    }
    .error {
      padding: 1rem;
      background: #fef2f2;
      border: 1px solid #ef4444;
      border-radius: 8px;
      color: #dc2626;
    }
  `]
})
export class UserListComponent implements OnInit {
  users: User[] = [];
  loading = false;
  error: string | null = null;

  constructor(private userService: UserService) {}

  ngOnInit() {
    this.loadUsers();
  }

  loadUsers() {
    this.loading = true;
    this.error = null;

    // ✅ Subscribe zu Observable
    this.userService.getUsers().subscribe({
      next: (users) => {
        this.users = users;
        this.loading = false;
      },
      error: (err) => {
        this.error = 'Fehler beim Laden der User';
        this.loading = false;
        console.error('Error:', err);
      }
    });
  }
}

Wichtig:

  • .subscribe() startet den Request!
  • next: - Success Handler
  • error: - Error Handler
  • Immer Loading & Error States implementieren!

Tipps & Tricks

Type-Safety mit Interfaces

Ohne Interface (BAD):

getUsers(): Observable<any> {  // ❌ any = no type safety
  return this.http.get(this.apiUrl);
}

Mit Interface (GOOD):

getUsers(): Observable<User[]> {  // ✅ Type-safe!
  return this.http.get<User[]>(this.apiUrl);
}

Nutze IMMER Interfaces für API Responses!

Async Pipe Alternative

Statt Subscribe:

export class UserListComponent {
  users$ = this.userService.getUsers();  // ✅ Observable direkt

  constructor(private userService: UserService) {}
}
<div *ngFor="let user of users$ | async">
  {{ user.name }}
</div>

Async Pipe macht automatisch subscribe/unsubscribe!

Environment Variables für API URLs

environments/environment.ts:

export const environment = {
  production: false,
  apiUrl: 'https://api.example.com'
};

Service:

import { environment } from '../environments/environment';

@Injectable({ providedIn: 'root' })
export class UserService {
  private apiUrl = environment.apiUrl;
  // ...
}

POST Requests - Daten erstellen

POST erstellt neue Daten auf dem Server.

Create User

user.service.ts:

createUser(user: Partial<User>): Observable<User> {
  return this.http.post<User>(this.apiUrl, user);
}

create-user.component.ts:

import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { UserService } from './services/user.service';

@Component({
  selector: 'app-create-user',
  template: `
    <form [formGroup]="form" (ngSubmit)="onSubmit()">
      <h2>Neuen User erstellen</h2>

      <input formControlName="name" placeholder="Name">
      <input formControlName="email" placeholder="Email">
      <input formControlName="username" placeholder="Username">

      <button
        type="submit"
        [disabled]="form.invalid || loading"
      >
        {{ loading ? 'Wird erstellt...' : 'User erstellen' }}
      </button>

      <div *ngIf="error" class="error">{{ error }}</div>
      <div *ngIf="success" class="success">User erfolgreich erstellt!</div>
    </form>
  `
})
export class CreateUserComponent {
  form = this.fb.group({
    name: ['', Validators.required],
    email: ['', [Validators.required, Validators.email]],
    username: ['', Validators.required]
  });

  loading = false;
  error: string | null = null;
  success = false;

  constructor(
    private fb: FormBuilder,
    private userService: UserService
  ) {}

  onSubmit() {
    if (this.form.invalid) return;

    this.loading = true;
    this.error = null;
    this.success = false;

    // ✅ POST Request
    this.userService.createUser(this.form.value).subscribe({
      next: (createdUser) => {
        console.log('User created:', createdUser);
        this.success = true;
        this.loading = false;
        this.form.reset();
      },
      error: (err) => {
        this.error = 'Fehler beim Erstellen des Users';
        this.loading = false;
        console.error('Error:', err);
      }
    });
  }
}

POST Body: Form Data wird automatisch als JSON gesendet!

PUT/PATCH Requests - Daten updaten

PUT: Komplette Ressource ersetzen PATCH: Nur bestimmte Felder updaten

Update User

user.service.ts:

// PUT - Komplettes Update
updateUser(id: number, user: User): Observable<User> {
  return this.http.put<User>(`${this.apiUrl}/${id}`, user);
}

// PATCH - Partielles Update
patchUser(id: number, updates: Partial<User>): Observable<User> {
  return this.http.patch<User>(`${this.apiUrl}/${id}`, updates);
}

Komponente:

updateUserEmail(userId: number, newEmail: string) {
  this.loading = true;

  // ✅ PATCH nur Email
  this.userService.patchUser(userId, { email: newEmail }).subscribe({
    next: (updatedUser) => {
      console.log('User updated:', updatedUser);
      this.loading = false;
    },
    error: (err) => {
      this.error = 'Update fehlgeschlagen';
      this.loading = false;
    }
  });
}

DELETE Requests - Daten löschen

Delete User

user.service.ts:

deleteUser(id: number): Observable<void> {
  return this.http.delete<void>(`${this.apiUrl}/${id}`);
}

Komponente:

deleteUser(userId: number) {
  if (!confirm('User wirklich löschen?')) return;

  this.loading = true;

  // ✅ DELETE Request
  this.userService.deleteUser(userId).subscribe({
    next: () => {
      console.log('User deleted');
      // Remove from local array
      this.users = this.users.filter(u => u.id !== userId);
      this.loading = false;
    },
    error: (err) => {
      this.error = 'Löschen fehlgeschlagen';
      this.loading = false;
    }
  });
}

Query Parameters & Headers

Query Parameters

URL: /users?role=admin&active=true

getUsers(role?: string, active?: boolean): Observable<User[]> {
  // ✅ Option 1: HttpParams
  let params = new HttpParams();
  if (role) params = params.set('role', role);
  if (active !== undefined) params = params.set('active', active.toString());

  return this.http.get<User[]>(this.apiUrl, { params });
}

// ✅ Option 2: Object (einfacher!)
getUsersSimple(role?: string): Observable<User[]> {
  return this.http.get<User[]>(this.apiUrl, {
    params: { role: role || '' }
  });
}

Usage:

// GET /users?role=admin&active=true
this.userService.getUsers('admin', true).subscribe(users => {
  console.log(users);
});

Custom Headers

getUsersWithAuth(): Observable<User[]> {
  const headers = new HttpHeaders({
    'Authorization': 'Bearer my-token-123',
    'Custom-Header': 'value'
  });

  return this.http.get<User[]>(this.apiUrl, { headers });
}

Error Handling

Proper Error Handling

user.service.ts:

import { catchError, throwError } from 'rxjs';
import { HttpErrorResponse } from '@angular/common/http';

@Injectable({ providedIn: 'root' })
export class UserService {

  getUsers(): Observable<User[]> {
    return this.http.get<User[]>(this.apiUrl).pipe(
      catchError(this.handleError)
    );
  }

  private handleError(error: HttpErrorResponse) {
    let errorMessage = 'Ein Fehler ist aufgetreten';

    if (error.error instanceof ErrorEvent) {
      // Client-seitiger Error
      errorMessage = `Client Error: ${error.error.message}`;
    } else {
      // Server-seitiger Error
      switch (error.status) {
        case 400:
          errorMessage = 'Ungültige Anfrage';
          break;
        case 401:
          errorMessage = 'Nicht authentifiziert';
          break;
        case 403:
          errorMessage = 'Zugriff verweigert';
          break;
        case 404:
          errorMessage = 'Ressource nicht gefunden';
          break;
        case 500:
          errorMessage = 'Server-Fehler';
          break;
        default:
          errorMessage = `Server Error: ${error.status}`;
      }
    }

    console.error('HTTP Error:', error);
    return throwError(() => new Error(errorMessage));
  }
}

Komponente:

loadUsers() {
  this.userService.getUsers().subscribe({
    next: (users) => this.users = users,
    error: (err) => this.error = err.message  // ✅ Formatted Error Message
  });
}

Häufige Fehler

Fehler 1: HttpClientModule nicht importiert

Problem:

Error: NullInjectorError: No provider for HttpClient!

Lösung:

// app.module.ts
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  imports: [HttpClientModule]  // ✅ Import hinzufügen
})

Fehler 2: Subscribe vergessen

Problem:

// Request wird NIE ausgeführt!
this.userService.getUsers();  // ❌ Kein subscribe!

Lösung:

this.userService.getUsers().subscribe(users => {
  console.log(users);  // ✅ Jetzt wird Request ausgeführt
});

Observables sind LAZY! Ohne .subscribe() passiert NICHTS!

Fehler 3: Unsubscribe vergessen

Problem:

ngOnInit() {
  // ❌ Memory Leak!
  this.userService.getUsers().subscribe(users => {
    this.users = users;
  });
}

Lösung 1: Async Pipe

users$ = this.userService.getUsers();  // ✅ Kein subscribe
<div *ngFor="let user of users$ | async">

Lösung 2: Unsubscribe

subscription!: Subscription;

ngOnInit() {
  this.subscription = this.userService.getUsers().subscribe(...);
}

ngOnDestroy() {
  this.subscription?.unsubscribe();  // ✅ Cleanup
}

Lösung 3: takeUntil

private destroy$ = new Subject<void>();

ngOnInit() {
  this.userService.getUsers()
    .pipe(takeUntil(this.destroy$))
    .subscribe(users => this.users = users);
}

ngOnDestroy() {
  this.destroy$.next();
  this.destroy$.complete();
}

Fehler 4: Fehlendes Error Handling

Problem:

this.http.get(url).subscribe(data => {
  // ❌ Kein Error Handler!
  console.log(data);
});

Lösung:

this.http.get(url).subscribe({
  next: (data) => console.log(data),
  error: (err) => console.error(err)  // ✅ Error Handler
});

HTTP Interceptors

Interceptors manipulieren alle HTTP Requests/Responses!

Use Cases:

  • ✅ Auth Token zu allen Requests hinzufügen
  • ✅ Loading Spinner global
  • ✅ Error Handling
  • ✅ Logging
  • ✅ Retry Logic

Auth Interceptor (Add Token to all Requests)

interceptors/auth.interceptor.ts:

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // Get token (z.B. from localStorage or AuthService)
    const token = localStorage.getItem('auth_token');

    if (token) {
      // ✅ Clone Request und füge Authorization Header hinzu
      const clonedReq = req.clone({
        setHeaders: {
          Authorization: `Bearer ${token}`
        }
      });

      return next.handle(clonedReq);
    }

    // Kein Token → Original Request
    return next.handle(req);
  }
}

In app.module.ts registrieren:

import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './interceptors/auth.interceptor';

@NgModule({
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true  // ✅ Wichtig! Mehrere Interceptors möglich
    }
  ]
})
export class AppModule { }

Jetzt hat JEDER Request automatisch den Authorization Header!

Loading Interceptor

interceptors/loading.interceptor.ts:

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';
import { finalize } from 'rxjs/operators';
import { LoadingService } from '../services/loading.service';

@Injectable()
export class LoadingInterceptor implements HttpInterceptor {

  constructor(private loadingService: LoadingService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // ✅ Request startet → Loading anzeigen
    this.loadingService.show();

    return next.handle(req).pipe(
      // ✅ Request fertig (Success oder Error) → Loading verstecken
      finalize(() => this.loadingService.hide())
    );
  }
}

services/loading.service.ts:

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class LoadingService {
  private loadingSubject = new BehaviorSubject<boolean>(false);
  loading$ = this.loadingSubject.asObservable();

  show() {
    this.loadingSubject.next(true);
  }

  hide() {
    this.loadingSubject.next(false);
  }
}

app.component.ts:

export class AppComponent {
  loading$ = this.loadingService.loading$;

  constructor(private loadingService: LoadingService) {}
}

app.component.html:

<div *ngIf="loading$ | async" class="loading-spinner">
  Lädt...
</div>

<router-outlet></router-outlet>

Jetzt erscheint bei JEDEM HTTP Request automatisch ein Loading Spinner! 🎉

RxJS Operators für HTTP

map - Response transformieren

getUsers(): Observable<string[]> {
  return this.http.get<User[]>(this.apiUrl).pipe(
    map(users => users.map(u => u.name))  // ✅ Nur Namen zurückgeben
  );
}

catchError - Error Handling

getUsers(): Observable<User[]> {
  return this.http.get<User[]>(this.apiUrl).pipe(
    catchError(error => {
      console.error('Error:', error);
      return of([]);  // ✅ Empty Array zurückgeben bei Error
    })
  );
}

retry - Automatisch wiederholen

import { retry } from 'rxjs/operators';

getUsers(): Observable<User[]> {
  return this.http.get<User[]>(this.apiUrl).pipe(
    retry(3),  // ✅ Bei Fehler 3x wiederholen
    catchError(this.handleError)
  );
}

debounceTime - Search mit Delay

Search Input mit Auto-Complete:

export class SearchComponent implements OnInit {
  searchControl = new FormControl('');
  results$!: Observable<User[]>;

  constructor(private userService: UserService) {}

  ngOnInit() {
    this.results$ = this.searchControl.valueChanges.pipe(
      debounceTime(300),  // ✅ Warte 300ms nach letztem Keystroke
      distinctUntilChanged(),  // ✅ Nur wenn Wert sich ändert
      switchMap(query => this.userService.searchUsers(query || ''))
    );
  }
}
<input [formControl]="searchControl" placeholder="Suche...">

<div *ngFor="let user of results$ | async">
  {{ user.name }}
</div>

forkJoin - Multiple Requests parallel

import { forkJoin } from 'rxjs';

loadAllData() {
  forkJoin({
    users: this.userService.getUsers(),
    posts: this.postService.getPosts(),
    comments: this.commentService.getComments()
  }).subscribe(({ users, posts, comments }) => {
    console.log('All loaded!', users, posts, comments);
  });
}

Alle 3 Requests laufen parallel! Callback erst wenn ALLE fertig!

🔍

Debugging & Troubleshooting

Network Tab nutzen (Chrome DevTools)

F12 → Network Tab:

  1. Filter: XHR für HTTP Requests
  2. Request URL: Prüfe ob URL korrekt ist
  3. Status Code: 200 = OK, 404 = Not Found, 500 = Server Error
  4. Request Headers: Prüfe Authorization, Content-Type
  5. Response: Prüfe was Server zurückgibt

CORS Errors

Problem:

Access to XMLHttpRequest blocked by CORS policy

Bedeutung: Backend erlaubt keine Requests von deiner Domain!

Lösung:

  • Backend muss CORS Header setzen: Access-Control-Allow-Origin: *
  • Oder: Proxy in Angular nutzen (Development)

Development Proxy:

proxy.conf.json:

{
  "/api": {
    "target": "http://localhost:3000",
    "secure": false
  }
}

angular.json:

"serve": {
  "options": {
    "proxyConfig": "proxy.conf.json"
  }
}
ng serve  # Proxy ist aktiv

Requests zu /api/users werden zu http://localhost:3000/api/users weitergeleitet!

HTTP Request debuggen

getUsers(): Observable<User[]> {
  return this.http.get<User[]>(this.apiUrl).pipe(
    tap(response => console.log('Response:', response)),  // ✅ Debug
    catchError(error => {
      console.error('Error:', error);  // ✅ Debug
      return throwError(() => error);
    })
  );
}

Komplettes Beispiel: Todo API Service

todo.service.ts:

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, retry, map } from 'rxjs/operators';

export interface Todo {
  id: number;
  title: string;
  completed: boolean;
  userId: number;
}

@Injectable({
  providedIn: 'root'
})
export class TodoService {
  private apiUrl = 'https://jsonplaceholder.typicode.com/todos';

  constructor(private http: HttpClient) {}

  // GET all todos
  getTodos(): Observable<Todo[]> {
    return this.http.get<Todo[]>(this.apiUrl).pipe(
      retry(2),  // Bei Fehler 2x wiederholen
      catchError(this.handleError)
    );
  }

  // GET single todo
  getTodoById(id: number): Observable<Todo> {
    return this.http.get<Todo>(`${this.apiUrl}/${id}`).pipe(
      catchError(this.handleError)
    );
  }

  // GET todos by user
  getTodosByUser(userId: number): Observable<Todo[]> {
    return this.http.get<Todo[]>(this.apiUrl, {
      params: { userId: userId.toString() }
    }).pipe(
      catchError(this.handleError)
    );
  }

  // POST new todo
  createTodo(todo: Partial<Todo>): Observable<Todo> {
    return this.http.post<Todo>(this.apiUrl, todo).pipe(
      catchError(this.handleError)
    );
  }

  // PUT update todo
  updateTodo(id: number, todo: Todo): Observable<Todo> {
    return this.http.put<Todo>(`${this.apiUrl}/${id}`, todo).pipe(
      catchError(this.handleError)
    );
  }

  // PATCH partial update
  toggleTodo(id: number, completed: boolean): Observable<Todo> {
    return this.http.patch<Todo>(`${this.apiUrl}/${id}`, { completed }).pipe(
      catchError(this.handleError)
    );
  }

  // DELETE todo
  deleteTodo(id: number): Observable<void> {
    return this.http.delete<void>(`${this.apiUrl}/${id}`).pipe(
      catchError(this.handleError)
    );
  }

  // Stats (derived)
  getTodoStats(userId: number): Observable<{ total: number; completed: number; active: number }> {
    return this.getTodosByUser(userId).pipe(
      map(todos => ({
        total: todos.length,
        completed: todos.filter(t => t.completed).length,
        active: todos.filter(t => !t.completed).length
      }))
    );
  }

  // Error Handler
  private handleError(error: HttpErrorResponse) {
    let errorMessage = 'Ein Fehler ist aufgetreten';

    if (error.error instanceof ErrorEvent) {
      errorMessage = `Client Error: ${error.error.message}`;
    } else {
      errorMessage = `Server Error (${error.status}): ${error.message}`;
    }

    console.error('HTTP Error:', error);
    return throwError(() => new Error(errorMessage));
  }
}

todo-list.component.ts:

import { Component, OnInit } from '@angular/core';
import { TodoService, Todo } from './services/todo.service';

@Component({
  selector: 'app-todo-list',
  template: `
    <div class="todo-list">
      <h1>Todos (User 1)</h1>

      <!-- Loading -->
      <div *ngIf="loading" class="loading">Lädt...</div>

      <!-- Error -->
      <div *ngIf="error" class="error">{{ error }}</div>

      <!-- Todos -->
      <div *ngIf="!loading && !error">
        <div class="stats">
          <span>Total: {{ todos.length }}</span>
          <span>Completed: {{ completedCount }}</span>
          <span>Active: {{ activeCount }}</span>
        </div>

        <div *ngFor="let todo of todos" class="todo-item">
          <input
            type="checkbox"
            [checked]="todo.completed"
            (change)="toggleTodo(todo)"
          >
          <span [class.completed]="todo.completed">
            {{ todo.title }}
          </span>
          <button (click)="deleteTodo(todo.id)">Delete</button>
        </div>
      </div>
    </div>
  `
})
export class TodoListComponent implements OnInit {
  todos: Todo[] = [];
  loading = false;
  error: string | null = null;

  constructor(private todoService: TodoService) {}

  ngOnInit() {
    this.loadTodos();
  }

  loadTodos() {
    this.loading = true;
    this.error = null;

    this.todoService.getTodosByUser(1).subscribe({
      next: (todos) => {
        this.todos = todos;
        this.loading = false;
      },
      error: (err) => {
        this.error = err.message;
        this.loading = false;
      }
    });
  }

  toggleTodo(todo: Todo) {
    this.todoService.toggleTodo(todo.id, !todo.completed).subscribe({
      next: (updatedTodo) => {
        const index = this.todos.findIndex(t => t.id === todo.id);
        this.todos[index] = updatedTodo;
      },
      error: (err) => console.error('Toggle failed:', err)
    });
  }

  deleteTodo(id: number) {
    this.todoService.deleteTodo(id).subscribe({
      next: () => {
        this.todos = this.todos.filter(t => t.id !== id);
      },
      error: (err) => console.error('Delete failed:', err)
    });
  }

  get completedCount() {
    return this.todos.filter(t => t.completed).length;
  }

  get activeCount() {
    return this.todos.filter(t => !t.completed).length;
  }
}
🎯

Zusammenfassung

Du hast gelernt:

  • HttpClient für HTTP-Kommunikation mit Backend-APIs
  • HttpClientModule importieren
  • GET Requests zum Laden von Daten
  • POST Requests zum Erstellen von Daten
  • PUT/PATCH Requests zum Updaten
  • DELETE Requests zum Löschen
  • Query Parameters und Headers
  • Error Handling mit catchError
  • Interceptors für globale Request/Response Manipulation
  • RxJS Operators: map, retry, debounceTime, forkJoin
  • ✅ Loading & Error States für bessere UX

Key Takeaways:

  • HttpClient gibt Observables zurück (lazy!)
  • .subscribe() startet Request
  • Type-safe mit Interfaces: http.get<User[]>()
  • Error Handling ist Pflicht
  • Interceptors für Auth Token, Loading, Logging
  • Async Pipe oder Unsubscribe!

Service Pattern:

@Injectable({ providedIn: 'root' })
export class DataService {
  constructor(private http: HttpClient) {}

  getData(): Observable<Data[]> {
    return this.http.get<Data[]>(url).pipe(
      retry(2),
      catchError(this.handleError)
    );
  }

  private handleError(error: HttpErrorResponse) {
    return throwError(() => new Error('Error!'));
  }
}

HTTP gemeistert! Zeit für ein komplettes Projekt! 🎯

Quiz

📝 Quiz

Warum muss man .subscribe() aufrufen?

📝 Quiz

Was macht ein HTTP Interceptor?

📝 Quiz

Was ist der Unterschied zwischen PUT und PATCH?

Übungen

Übung 1: User CRUD Service

Erstelle einen kompletten User Service mit:

  • getUsers() - Alle User laden
  • getUserById(id) - Einzelner User
  • createUser(user) - User erstellen
  • updateUser(id, user) - User updaten
  • deleteUser(id) - User löschen
  • Error Handling für alle Methoden

API: https://jsonplaceholder.typicode.com/users

Übung 2: Search mit Debounce

Erstelle eine Search-Komponente die:

  • Input Field für Suche
  • Bei jedem Keystroke API Call (mit 300ms debounce)
  • Ergebnisse live anzeigen
  • Loading State während Request
  • "Keine Ergebnisse" wenn nichts gefunden

API: https://jsonplaceholder.typicode.com/users?name_like={query}

Übung 3: Auth Interceptor

Erstelle einen Auth Interceptor der:

  • Token aus localStorage holt
  • Authorization Header zu allen Requests hinzufügt
  • Bei 401 Error automatisch zu /login redirectet
  • Registriere Interceptor in app.module.ts

Nächste Schritte

Im nächsten Artikel:

  • Projekt: Todo App
  • Alles zusammenbringen
  • Components, Services, Routing, Forms, HTTP
  • Complete CRUD Application
  • Best Practices

➡️ Weiter zu: Angular Todo App - Komplettes Projekt

HTTP gemeistert! Zeit für ein echtes Projekt! 🚀

AngularLektion 9 von 10
90% abgeschlossen
Lektion abgeschlossen!

Gut gemacht! 🎉

Du hast "Angular HTTP Client & APIs - Backend-Kommunikation" abgeschlossen

Artikel bewerten

0.0 (0 Bewertungen)

Bitte einloggen um zu bewerten