Regresar al inicio

Olvida Axios: Construye tu propio wrapper en TypeScript

Usar librerías como Axios es común para manejar solicitudes HTTP, pero a menudo incluyen funcionalidades que, en lo personal, no necesito. En este artículo, te mostraré cómo construir un wrapper HTTP personalizado en TypeScript, pensado para ser ligero, fácil de usar y adaptable a tus necesidades.

Dentro de un archivo de utilidad, definimos la interfaz ApiResponse. Esta interfaz representa la estructura de una respuesta HTTP, adaptala según la definición de tu API.

serverRequest.ts
interface ApiResponse<T> {
  data: T;
  success: boolean;
  message: string;
  errors: any[];
}

Luego, creamos una clase Request con métodos estáticos para cada verbo HTTP.

serverRequest.ts
class Request {
  async get<T>(url: string): Promise<T> {}

  async post<T>(url: string, body: any): Promise<T> {}

  async put<T>(url: string, body: any): Promise<T> {}

  async delete<T>(url: string): Promise<T> {}
}

export default Request;

Para cada método, la estructura es similar: Asignamos la respuesta de la API fetch a una variable, y luego procesamos dicha respuesta según la interfaz definida anteriormente.

serverRequest.ts
async get<T>(url: string): Promise<T> {
    const res = await fetch(url);
    const data: ApiResponse<T> = await res.json();
    
    if (data.success) {
        return data.data;
    } else {
        throw new Error(data.message);
    }
}
serverRequest.ts
async post<T>(url: string, body: any): Promise<T> {
    const res = await fetch(url, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify(body),
    });
    const data: ApiResponse<T> = await res.json();
    
    if (data.success) {
        return data.data;
    } else {
        throw new Error(data.message);
    }
}
serverRequest.ts
async put<T>(url: string, body: any): Promise<T> {
    const res = await fetch(url, {
        method: "PUT",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify(body),
    });
    const data: ApiResponse<T> = await res.json();
    
    if (data.success) {
        return data.data;
    } else {
        throw new Error(data.message);
    }
}
serverRequest.ts
async delete<T>(url: string): Promise<T> {
    const res = await fetch(url, {
        method: "DELETE",
    });
    const data: ApiResponse<T> = await res.json();
    
    if (data.success) { 
        return data.data;
    } else {
        throw new Error(data.message);
    }
}

Notese que cada método lleva una estructura similar, lo que facilita su mantenimiento permitiendo agregar funcionalidades adicionales, así como nuevos métodos según sea necesario. También es importante destacar el uso de los tipos genéricos T, lo que permite que el wrapper sea flexible y se adapte a diferentes tipos de datos según la respuesta de la API.

Por último, usamos el wrapper en nuestro código. Es importante destacar que debido a su definición, necesitamos envolverlo en una función asíncrona y manejar los errores adecuadamente.

example.ts
async function fetchData() {
    try {
        const data = await Request.get("/api/data");
        console.log(data);
    } catch (error) {
        console.error("Error fetching data:", error);
    }
}

Hasta el próximo blog!