Cómo usar Fetch API para consumir APIs REST en JavaScript

D
DanisCh
(Actualizado: ) • 6 min de lectura
Cómo usar Fetch API para consumir APIs REST en JavaScript
JavaScript

Hace algunos años, hacer una petición HTTP en JavaScript desde el navegador requería lidiar con el tosco y verboso XMLHttpRequest (XHR). Era tan engorroso que librerías como jQuery o Axios se volvieron indispensables solo para simplificar este proceso. Sin embargo, el lenguaje evolucionó y trajo consigo una solución nativa, elegante y basada en Promesas: la Fetch API.

La API fetch() proporciona una interfaz global moderna para buscar (hacer fetch a) recursos a través de la red. Hoy en día, es el estándar de oro para consumir APIs REST en el Front-end (y ahora también en Node.js de forma nativa a partir de la versión 18).

En esta guía definitiva, aprenderemos desde la sintaxis más básica de fetch hasta casos de uso avanzados, manejo de errores, interceptación de respuestas y envío de diferentes tipos de datos.

1. Anatomía de la función Fetch

La función fetch() toma un argumento obligatorio: la ruta (URL) del recurso que quieres obtener. Por defecto, fetch siempre realiza una petición GET.

Dado que fetch() devuelve una Promesa, podemos manejar la respuesta usando el clásico encadenamiento de .then() o la sintaxis moderna de async/await (la cual es la recomendada en proyectos actuales).

Ejemplo básico con async/await


async function obtenerUsuarios() {
    try {
        const respuesta = await fetch('https://jsonplaceholder.typicode.com/users');
        const datos = await respuesta.json(); // Transformar el cuerpo a JSON
        
        console.log(datos);
    } catch (error) {
        console.error('Ocurrió un error en la red:', error);
    }
}

obtenerUsuarios();

Nota importante: El primer await resuelve la respuesta HTTP (cabeceras, código de estado, etc.), pero el cuerpo de la respuesta es un flujo de datos (ReadableStream). Por eso necesitamos un segundo await para leer el flujo y parsearlo a JSON con el método .json().

2. El objeto Response: Entendiendo qué devuelve Fetch

La promesa de fetch no se rechaza (no cae en el bloque catch) en caso de errores HTTP (como un 404 No Encontrado o un 500 Error Interno del Servidor). Solo se rechaza si hay un fallo de red o la petición no pudo completarse (ej. falta de conexión a internet).

Por lo tanto, es tu responsabilidad verificar si la respuesta fue exitosa usando la propiedad response.ok (que es true si el código de estado HTTP está entre 200 y 299).

El patrón correcto de manejo de errores


async function obtenerUsuarioSeguro(id) {
    try {
        const respuesta = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
        
        // ¡Validación crucial!
        if (!respuesta.ok) {
            throw new Error(`Error HTTP: ${respuesta.status} - ${respuesta.statusText}`);
        }
        
        const datos = await respuesta.json();
        return datos;
    } catch (error) {
        // Aquí atrapamos tanto errores de red como errores HTTP lanzados manualmente
        console.error('Fallo en la petición:', error.message);
    }
}

3. Configurando opciones: El segundo parámetro de Fetch

Para hacer algo más que un simple GET, necesitamos pasarle a fetch() un segundo argumento: un objeto de configuración (options). Aquí podemos definir el método HTTP (POST, PUT, DELETE), configurar las cabeceras (Headers) y enviar el cuerpo de la petición (Body).

Realizando una petición POST (Creando recursos)

Cuando envías datos al servidor, normalmente debes indicarle qué tipo de datos estás enviando mediante la cabecera Content-Type, y transformar tu objeto de JavaScript a una cadena JSON usando JSON.stringify().


async function crearPost(nuevoPost) {
    const url = 'https://jsonplaceholder.typicode.com/posts';
    const opciones = {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer MI_TOKEN_DE_ACCESO' // Ejemplo de autenticación
        },
        body: JSON.stringify(nuevoPost)
    };

    try {
        const respuesta = await fetch(url, opciones);
        
        if (!respuesta.ok) throw new Error(`Error: ${respuesta.status}`);
        
        const resultado = await respuesta.json();
        console.log('Post creado con éxito:', resultado);
        return resultado;
    } catch (error) {
        console.error('Error al crear el post:', error);
    }
}

// Uso de la función
crearPost({
    title: 'Amo JavaScript',
    body: 'Fetch API es genial',
    userId: 1
});

Actualizando y Eliminando (PUT / DELETE)

Los métodos PUT (o PATCH) y DELETE siguen exactamente el mismo patrón que POST, solo cambiando el method.


// Ejemplo rápido de un DELETE
async function borrarPost(postId) {
    const respuesta = await fetch(`https://jsonplaceholder.typicode.com/posts/${postId}`, {
        method: 'DELETE'
    });
    
    if (respuesta.ok) {
        console.log('Post eliminado exitosamente');
    }
}

4. Trabajando con Formularios: FormData

Muchas veces no queremos enviar JSON, sino enviar datos simulando que provienen de un formulario HTML (por ejemplo, para subir imágenes o archivos). Para esto utilizamos la interfaz FormData.

Curiosidad de Fetch: Cuando usas FormData como body, ¡NO debes establecer manualmente la cabecera Content-Type! Fetch lo hace por ti automáticamente y añade el "boundary" necesario para datos multipart.


async function subirImagen(archivoInput) {
    const formData = new FormData();
    // Agregamos un archivo desde un input 
    formData.append('avatar', archivoInput.files[0]);
    formData.append('username', 'usuario_anonimo');

    try {
        const respuesta = await fetch('/api/upload', {
            method: 'POST',
            body: formData // Fetch configura automáticamente el Content-Type
        });
        
        const datos = await respuesta.json();
        console.log('Subida completada', datos);
    } catch (error) {
        console.error('Error subiendo archivo', error);
    }
}

5. Abortando Peticiones: AbortController

A diferencia de XMLHttpRequest, Fetch no tiene un método .abort() incorporado en la propia promesa. Para cancelar una petición en curso (por ejemplo, si el usuario navega a otra página antes de que termine de cargar), utilizamos AbortController.


const controller = new AbortController();
const signal = controller.signal;

async function buscarResultados(query) {
    try {
        // Pasamos la 'signal' en el objeto de opciones
        const respuesta = await fetch(`https://api.ejemplo.com/search?q=${query}`, { signal });
        const datos = await respuesta.json();
        console.log(datos);
    } catch (error) {
        if (error.name === 'AbortError') {
            console.log('Petición cancelada por el usuario o sistema');
        } else {
            console.error('Error de red:', error);
        }
    }
}

// Iniciamos la petición
buscarResultados('javascript');

// Si por alguna razón necesitamos cancelarla antes de que responda:
// controller.abort();

6. CORS: El temido bloqueo de origen cruzado

Cuando intentas hacer fetch a una API alojada en un dominio diferente al tuyo, es muy probable que te encuentres con un error de CORS (Cross-Origin Resource Sharing).

CORS es una medida de seguridad implementada por los navegadores. Para que un cliente pueda leer la respuesta de una API en un dominio diferente, el servidor de esa API debe responder con cabeceras específicas que autoricen a tu dominio (ej. Access-Control-Allow-Origin: *).

¿Cómo solucionarlo desde JavaScript? La triste realidad es que no puedes arreglar un error CORS desde el código cliente (con Fetch). La solución debe implementarse en el backend que aloja la API, configurando las políticas CORS correctamente.

Conclusión: ¿Axios o Fetch?

Hoy en día, la Fetch API es increíblemente robusta. Para la inmensa mayoría de los proyectos web, no necesitas una librería externa para realizar peticiones HTTP. fetch() viene incluido en el navegador, no aumenta el tamaño de tu bundle y la sintaxis moderna con async/await es extremadamente limpia.

Sin embargo, librerías como Axios siguen siendo populares porque ofrecen utilidades "Out of the box": transforman JSON automáticamente, rechazan la promesa en errores 404 o 500, permiten configurar interceptores (interceptors) fácilmente y manejan tiempos de espera (timeouts) nativamente. Aún así, es fundamental dominar Fetch, ya que es el estándar universal del que beben los navegadores modernos.

¿Te ha gustado esta entrada?

Compártela con tus compañeros para que también sigan aprendiendo.

Comunidad y Comentarios

0 COMENTARIOS

No hay comentarios todavía. Sé el primero en compartir tu opinión.

Escribe tu opinión
Respondiendo a