La API Fetch ha revolucionado la forma en que interactuamos con servidores web desde JavaScript, reemplazando eficazmente al obsoleto XMLHttpRequest (XHR). Integrada nativamente en todos los navegadores modernos y en Node.js 18+, esta API se basa en promesas (Promises), lo que resulta en un código más limpio y fácil de entender. Si aún recurres a librerías como jQuery.ajax() o Axios para peticiones sencillas, es hora de reconsiderarlo y abrazar el poder nativo de Fetch.
Realizando Peticiones GET Sencillas
// GET simple
const response = await fetch('https://api.ejemplo.com/usuarios');
const usuarios = await response.json();
console.log(usuarios);
// Con manejo de errores
async function obtenerUsuarios() {
const response = await fetch('https://api.ejemplo.com/usuarios');
if (!response.ok) {
// Lanzamos un error si la respuesta HTTP no es exitosa (e.g., 404, 500)
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
// Parseamos la respuesta JSON
return response.json();
}
Punto Clave: Es fundamental entender que fetch() solo rechaza la promesa en caso de errores de red (cuando el servidor no es alcanzable). Las respuestas con códigos de error HTTP (como 404 "No encontrado" o 500 "Error del servidor") se consideran exitosas desde la perspectiva de la red. Por ello, debes verificar manualmente la propiedad response.ok para asegurarte de que la petición fue realmente exitosa a nivel de aplicación.
Peticiones POST, PUT y DELETE: Modificando Recursos
const response = await fetch('https://api.ejemplo.com/usuarios', {
method: 'POST', // Especificamos el método HTTP
headers: {
'Content-Type': 'application/json', // Indicamos que enviamos JSON
'Authorization': 'Bearer eyJhbGci...', // Token de autenticación (ejemplo)
},
body: JSON.stringify({ // Convertimos el objeto JavaScript a una cadena JSON
nombre: 'Alicia',
email: '[email protected]',
}),
});
const nuevoUsuario = await response.json(); // Procesamos la respuesta del servidor
await fetch('https://api.ejemplo.com/usuarios/123', { // Especificamos el ID del usuario a actualizar
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ nombre: 'Alicia Actualizada' }), // Enviamos los datos a actualizar
});
await fetch('https://api.ejemplo.com/usuarios/123', { // Especificamos el ID del usuario a eliminar
method: 'DELETE',
headers: { 'Authorization': 'Bearer token123' }, // Autorización necesaria
});
Interpretando Diferentes Tipos de Respuestas
const response = await fetch(url);
// Para respuestas en formato JSON
const datosJson = await response.json();
// Para respuestas en texto plano
const textoPlano = await response.text();
// Para datos binarios (imágenes, archivos)
const objetoBlob = await response.blob();
// Para datos binarios crudos (ArrayBuffer)
const bufferBinario = await response.arrayBuffer();
// Para formularios enviados como 'multipart/form-data'
const datosFormulario = await response.formData();
// Metadatos de la respuesta
console.log(response.status); // Código de estado HTTP (ej: 200)
console.log(response.statusText); // Texto descriptivo del estado (ej: "OK")
console.log(response.ok); // Boolean: true si el código de estado está entre 200 y 299
console.log(response.headers.get('Content-Type')); // Obtener un encabezado específico
AbortController: Cancelando Peticiones en Curso
const controller = new AbortController();
// Programamos la cancelación de la petición después de 5 segundos
const timeoutId = setTimeout(() => controller.abort(), 5000);
try {
const response = await fetch('https://api.ejemplo.com/datos-pesados', {
signal: controller.signal, // Asociamos la señal del controlador
});
clearTimeout(timeoutId); // Si la respuesta llega antes, cancelamos el timeout
const data = await response.json();
// Procesar los datos...
} catch (err) {
if (err.name === 'AbortError') {
// Manejamos específicamente el error de cancelación
console.log('La petición fue cancelada por tiempo de espera');
} else {
// Lanzamos otros errores de red o de procesamiento
throw err;
}
}
Carga de Archivos con Fetch
const formularioDatos = new FormData();
// Añadimos el archivo seleccionado por el usuario
formularioDatos.append('avatar', inputArchivo.files[0]);
// Añadimos otros campos si son necesarios
formularioDatos.append('nombreUsuario', 'Alicia');
// Importante: No establecemos 'Content-Type'. El navegador lo hace automáticamente
// para 'multipart/form-data', incluyendo el 'boundary' necesario.
const response = await fetch('/api/upload-avatar', {
method: 'POST',
body: formularioDatos, // Pasamos el objeto FormData como cuerpo de la petición
});
Creando un Wrapper Reutilizable para Fetch
class ClienteApi {
constructor(baseUrl, token) {
this.baseUrl = baseUrl; // URL base de tu API
this.token = token; // Token de autenticación
}
async peticion(ruta, opciones = {}) {
const urlCompleta = `${this.baseUrl}${ruta}`;
const headersDefecto = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.token}`,
...opciones.headers, // Permite sobrescribir o añadir headers
};
const respuesta = await fetch(urlCompleta, {
...opciones, // Fusionamos las opciones pasadas
headers: headersDefecto,
});
if (!respuesta.ok) {
// Intentamos obtener un mensaje de error del cuerpo de la respuesta
const errorDetalle = await respuesta.json().catch(() => ({})); // Si no es JSON, obtenemos un objeto vacío
throw new Error(errorDetalle.mensaje || `Error HTTP ${respuesta.status}`);
}
return respuesta.json(); // Devolvemos la respuesta parseada
}
// Métodos de conveniencia
get(ruta) { return this.peticion(ruta); }
post(ruta, datos) { return this.peticion(ruta, { method: 'POST', body: JSON.stringify(datos) }); }
put(ruta, datos) { return this.peticion(ruta, { method: 'PUT', body: JSON.stringify(datos) }); }
delete(ruta) { return this.peticion(ruta, { method: 'DELETE' }); }
}
// Ejemplo de uso:
const miApi = new ClienteApi('https://api.ejemplo.com', 'miTokenSecreto123');
const todosLosUsuarios = await miApi.get('/usuarios');
const nuevoUsuarioCreado = await miApi.post('/usuarios', { nombre: 'Carlos' });
Errores Comunes a Evitar
- Olvido de la comprobación
response.ok: Las respuestas de error HTTP (4xx, 5xx) no lanzan excepciones automáticamente. Debes verificarresponse.okpara detectarlas. - Establecer manualmente
Content-TypeparaFormData: Al enviar datos de formulario (archivos, etc.), el navegador necesita generar un delimitador único (boundary). Dejar que el navegador lo haga es crucial. - Consumir el cuerpo de la respuesta múltiples veces: Métodos como
response.json()oresponse.text()solo pueden ser llamados una vez por respuesta. Almacena el resultado en una variable si necesitas usarlo varias veces. - No manejar
AbortError: Cuando se utilizaAbortController, la promesa rechazada tendrá la propiedadnameigual a'AbortError'. Es importante capturar y manejar este caso específico.
¡Prueba Nuestras Herramientas Gratuitas para Desarrolladores!
Formatea respuestas de API y codifica parámetros de solicitud de manera sencilla.