httpClient.ts•3.66 kB
import { ApiResponse, ApiError } from '../types/api.js';
import { API_CONFIG } from '../config/api.js';
/**
* Cliente HTTP base para realizar peticiones a la API
*/
export class HttpClient {
private baseURL: string;
private defaultHeaders: Record<string, string>;
constructor(baseURL?: string, headers?: Record<string, string>) {
this.baseURL = baseURL || API_CONFIG.baseURL;
this.defaultHeaders = { ...API_CONFIG.headers, ...headers };
}
/**
* Realiza una petición GET
*/
async get<T>(endpoint: string, params?: Record<string, any>): Promise<ApiResponse<T>> {
const url = this.buildUrl(endpoint, params);
try {
const response = await fetch(url, {
method: 'GET',
headers: this.defaultHeaders,
});
return await this.handleResponse<T>(response);
} catch (error) {
throw this.handleError(error);
}
}
/**
* Realiza una petición POST
*/
async post<T, K = any>(endpoint: string, data: K): Promise<ApiResponse<T>> {
const url = this.buildUrl(endpoint);
try {
const response = await fetch(url, {
method: 'POST',
headers: this.defaultHeaders,
body: JSON.stringify(data),
});
return await this.handleResponse<T>(response);
} catch (error) {
throw this.handleError(error);
}
}
/**
* Realiza una petición PUT
*/
async put<T, K = any>(endpoint: string, data: K): Promise<ApiResponse<T>> {
const url = this.buildUrl(endpoint);
try {
const response = await fetch(url, {
method: 'PUT',
headers: this.defaultHeaders,
body: JSON.stringify(data),
});
return await this.handleResponse<T>(response);
} catch (error) {
throw this.handleError(error);
}
}
/**
* Realiza una petición DELETE
*/
async delete<T>(endpoint: string): Promise<ApiResponse<T>> {
const url = this.buildUrl(endpoint);
try {
const response = await fetch(url, {
method: 'DELETE',
headers: this.defaultHeaders,
});
return await this.handleResponse<T>(response);
} catch (error) {
throw this.handleError(error);
}
}
/**
* Construye la URL completa
*/
private buildUrl(endpoint: string, params?: Record<string, any>): string {
const url = new URL(endpoint, this.baseURL);
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
url.searchParams.append(key, String(value));
}
});
}
return url.toString();
}
/**
* Maneja la respuesta de la API
*/
private async handleResponse<T>(response: Response): Promise<ApiResponse<T>> {
const contentType = response.headers.get('content-type');
let data: any;
if (contentType && contentType.includes('application/json')) {
data = await response.json();
} else {
data = await response.text();
}
if (!response.ok) {
throw new Error(`HTTP Error ${response.status}: ${data.message || response.statusText}`);
}
return data;
}
/**
* Maneja errores de la petición
*/
private handleError(error: any): ApiError {
if (error instanceof TypeError && error.message.includes('fetch')) {
return {
message: 'Error de conexión con la API',
status: 0,
details: error.message
};
}
return {
message: error.message || 'Error desconocido',
status: error.status || 500,
details: error
};
}
}
// Instancia singleton del cliente HTTP
export const httpClient = new HttpClient();