HTTP-Client in TypeScript: korrekte Anfragen und Antwortverarbeitung

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Warum ein korrekter HTTP-Client die Grundlage moderner Webanwendungen ist

Jede moderne Webanwendung interagiert über HTTP mit dem Server. Ob das Laden einer Produktliste, das Absenden eines Registrierungsformulars oder das Abrufen von Daten für ein Dashboard – all das läuft auf das Senden von HTTP-Anfragen hinaus. Mit TypeScript und seinem leistungsstarken Typsystem kann man nicht nur einen funktionierenden, sondern einen wirklich zuverlässigen und vorhersagbaren HTTP-Client erstellen.

In diesem Artikel werden wir durchgehen, wie man einen HTTP-Client in TypeScript erstellt, der Fehler behandelt, Antworten korrekt typisiert, Header verwaltet und Middleware unterstützt. Sie werden erfahren, warum die Verwendung von rohem fetch ohne Wrapper eine schlechte Praxis ist und wie man es in ein leistungsstarkes Werkzeug verwandelt.



Grundlagen: Vom rohen fetch zum typisierten Client

Beginnen wir mit einem einfachen Beispiel. Der eingebaute fetch in JavaScript ist ein guter Ausgangspunkt, aber er bietet keine bequeme Typisierung und Fehlerbehandlung. In TypeScript können wir ihn in eine eigene Funktion einwickeln.

Einfacher Wrapper für fetch

Erstellen wir eine Basisklasse HttpClient, die eine Basis-URL und Einstellungen akzeptiert.

interface RequestOptions {  headers?: Record<string, string>;  params?: Record<string, string>;}

class HttpClient { private baseUrl: string; private defaultHeaders: Record<string, string>;

constructor(baseUrl: string, defaultHeaders: Record<string, string> = {}) { this.baseUrl = baseUrl; this.defaultHeaders = defaultHeaders; }

private buildUrl(path: string, params?: Record<string, string>): string { const url = new URL(`${this.baseUrl}${path}`); if (params) { Object.entries(params).forEach(([key, value]) => url.searchParams.append(key, value)); } return url.toString(); }

async get<T>(path: string, options?: RequestOptions): Promise<T> { const url = this.buildUrl(path, options?.params); const response = await fetch(url, { method: 'GET', headers: { ...this.defaultHeaders, ...options?.headers }, });

if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); }

return response.json() as Promise<T>; }}

Jetzt gibt der Aufruf client.get<User>('/users/1') ein typisiertes Objekt User zurück. Das ist bereits viel besser als die Arbeit mit any.



Fortgeschrittene Fehlerbehandlung und Typisierung von Antworten

Einfaches Auslösen eines Fehlers ist nicht der beste Ansatz. Wir müssen zwischen Netzwerkfehlern, Serverfehlern (4xx, 5xx) und Fehlern beim Parsen von JSON unterscheiden. Erstellen wir eigene Fehlerklassen.

Erstellung von Fehlerklassen

class HttpError extends Error {  status: number;  body: any;

constructor(message: string, status: number, body?: any) { super(message); this.name = 'HttpError'; this.status = status; this.body = body; }}

class NetworkError extends Error { constructor(message: string) { super(message); this.name = 'NetworkError'; }}

class ParseError extends Error { constructor(message: string) { super(message); this.name = 'ParseError'; }}

Verbesserte Behandlung in der get-Methode

async get<T>(path: string, options?: RequestOptions): Promise<T> {  try {    const url = this.buildUrl(path, options?.params);    const response = await fetch(url, {      method: 'GET',      headers: { ...this.defaultHeaders, ...options?.headers },    });

let body: any; try { body = await response.json(); } catch { throw new ParseError('Failed to parse response as JSON'); }

if (!response.ok) { throw new HttpError( `Request failed with status ${response.status}`, response.status, body ); }

return body as T; } catch (er

Blogs

Buchempfehlungen