Client HTTP en TypeScript : requêtes correctes et traitement des réponses

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : pourquoi un client HTTP correct est la base d'une application web moderne

Toute application web moderne interagit avec le serveur via HTTP. Qu'il s'agisse de charger une liste de produits, d'envoyer un formulaire d'inscription ou de récupérer des données pour un tableau de bord, tout se résume à l'envoi de requêtes HTTP. Avec TypeScript, grâce à son puissant système de types, on peut construire non seulement un client HTTP fonctionnel, mais véritablement fiable et prévisible.

Dans cet article, nous allons voir comment créer un client HTTP en TypeScript qui gérera les erreurs, typera correctement les réponses, gérera les en-têtes et supportera les middleware. Vous découvrirez pourquoi utiliser du fetch brut sans wrapper est une mauvaise pratique, et comment le transformer en un outil puissant.



Les bases : du fetch brut au client typé

Commençons par un exemple de base. Le fetch natif en JavaScript est un bon point de départ, mais il ne fournit pas un typage pratique ni une gestion des erreurs. Avec TypeScript, nous pouvons l'envelopper dans notre propre fonction.

Un wrapper simple pour fetch

Créons une classe de base HttpClient qui acceptera une URL de base et des paramètres.

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>; }}

Maintenant, l'appel client.get<User>('/users/1') retournera un objet User typé. C'est déjà bien mieux que de travailler avec any.



Gestion avancée des erreurs et typage des réponses

Lancer simplement une erreur n'est pas la meilleure approche. Nous devons distinguer les erreurs réseau, les erreurs serveur (4xx, 5xx) et les erreurs d'analyse JSON. Créons nos propres classes d'erreur.

Création des classes d'erreur

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'; }}

Gestion améliorée dans la méthode get

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

Book Recommendations