Cliente HTTP en TypeScript: técnicas avanzadas de solicitudes

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Cliente HTTP avanzado en TypeScript: de solicitudes básicas a soluciones industriales



Al desarrollar aplicaciones web modernas en TypeScript, el trabajo con solicitudes HTTP va mucho más allá de una simple llamada a fetch. A nivel avanzado, no solo se requiere enviar solicitudes, sino también manejar correctamente los errores, gestionar los tiempos de espera, tipificar las respuestas, implementar interceptores e incluso almacenar datos en caché. En este artículo, analizaremos cómo construir un cliente HTTP robusto y flexible en TypeScript, adecuado para proyectos en producción.



1. Creación de un cliente HTTP tipificado utilizando genéricos (Generics)



Una de las principales ventajas de TypeScript es la tipificación estricta. Crearemos una clase base HttpClient que aceptará el tipo de respuesta a través de genéricos. Esto permitirá evitar errores en la etapa de compilación.



// types.tsexport interface ApiResponse<T> {  data: T;  status: number;  ok: boolean;}

export interface RequestConfig { headers?: Record<string, string>; params?: Record<string, string>; signal?: AbortSignal;}

// http-client.tsimport { ApiResponse, RequestConfig } from './types';

class HttpClient { private baseUrl: string;

constructor(baseUrl: string) { this.baseUrl = baseUrl; }

async get<T>(endpoint: string, config?: RequestConfig): Promise<ApiResponse<T>> { const url = new URL(`${this.baseUrl}${endpoint}`); if (config?.params) { Object.entries(config.params).forEach(([key, value]) => { url.searchParams.append(key, value); }); }

const response = await fetch(url.toString(), { method: 'GET', headers: { 'Content-Type': 'application/json', ...config?.headers, }, signal: config?.signal, });

const data: T = await response.json(); return { data, status: response.status, ok: response.ok, }; }

async post<T, U>(endpoint: string, body: U, config?: RequestConfig): Promise<ApiResponse<T>> { const response = await fetch(`${this.baseUrl}${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...config?.headers, }, body: JSON.stringify(body), signal: config?.signal, });

const data: T = await response.json(); return { data, status: response.status, ok: response.ok }; }}

export default HttpClient;


2. Manejo de errores y tiempos de espera



Un cliente avanzado debe manejar correctamente tanto los errores de red como los errores de lógica de negocio. Agregaremos un mecanismo de tiempo de espera usando AbortController y un manejo personalizado de errores.



// errors.tsexport class HttpError extends Error {  status: number;  data?: unknown;

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

export class TimeoutError extends Error { constructor(timeout: number) { super(`La solicitud superó el tiempo de espera de ${timeout}ms`); this.name = 'TimeoutError'; }}

// http-client.ts (complemento)async requestWithTimeout<T>( endpoint: string, options: RequestInit, timeout: number = 5000): Promise<ApiResponse<T>> { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout);

try { const response = await fetch(`${this.baseUrl}${endpoint}`, { ...options, signal: controller.signal, });

if (!response.ok) { const errorData = await response.json().catch(() => null); throw new HttpError( `HTTP ${response.status}: ${response.statusText}`, response.status, errorData ); }

const data: T = await response.json(); r

Blogs

Book Recommendations