Skip to main content

Ejemplos Completos de Fetch API

Estos ejemplos muestran cómo usar Fetch API para obtener, mostrar y procesar datos de APIs reales.

Ejemplo Completo del Proyecto

Este es el ejemplo real implementado en el proyecto que carga y muestra usuarios:
const btnUsersThen = document.getElementById("users-then");
const btnUsersAsync = document.getElementById("users-async");
const usersOutput = document.getElementById("users-output");
const url = "https://jsonplaceholder.typicode.com/users";

function printUsers(usersList) {
  var users = [];
  for (var i = 0; i < usersList.length; i++) {
    var user = usersList[i];
    var userDataFormatted = user.id + " - " + user.name + " (" + user.email + ")";
    users.push(userDataFormatted);
  }
  usersOutput.textContent = users.join("\n");
}

// Versión 1: Con .then()
function cargarUsuariosThen() {
  usersOutput.textContent = "Cargando...";
  fetch(url)
    .then(function (response) {
      if (!response.ok) {
        throw new Error("HTTP status " + response.status);
      }
      return response.json();
    })
    .then(function (data) {
      console.log("Users (.then):", data);
      printUsers(data);
    })
    .catch(function (error) {
      console.log(error);
      usersOutput.textContent = error;
    });
}

btnUsersThen.addEventListener("click", cargarUsuariosThen);

// Versión 2: Con async/await
async function cargarUsuariosAsync() {
  usersOutput.textContent = "Cargando...";
  try {
    var response = await fetch(url);
    if (!response.ok) {
      throw new Error("HTTP status " + response.status);
    }
    var data = await response.json();
    console.log("Users (async/await):", data);
    printUsers(data);
  } catch (error) {
    console.log(error);
    usersOutput.textContent = error;
  }
}

btnUsersAsync.addEventListener("click", cargarUsuariosAsync);
Este ejemplo muestra ambas formas de manejar Promises: .then() y async/await. La versión async/await es generalmente más fácil de leer.

Ejemplo: Tarjetas de Usuario

Créar tarjetas HTML dinámicas con datos de usuarios:
async function cargarTarjetasUsuarios() {
  const contenedor = document.getElementById('usuarios-contenedor');
  contenedor.innerHTML = '<p>Cargando usuarios...</p>';
  
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users');
    
    if (!response.ok) {
      throw new Error(`Error HTTP: ${response.status}`);
    }
    
    const usuarios = await response.json();
    
    // Limpiar el contenedor
    contenedor.innerHTML = '';
    
    // Crear una tarjeta para cada usuario
    usuarios.forEach(usuario => {
      const tarjeta = document.createElement('div');
      tarjeta.className = 'tarjeta-usuario';
      
      tarjeta.innerHTML = `
        <h3>${usuario.name}</h3>
        <p><strong>Email:</strong> ${usuario.email}</p>
        <p><strong>Teléfono:</strong> ${usuario.phone}</p>
        <p><strong>Ciudad:</strong> ${usuario.address.city}</p>
        <button onclick="verDetalles(${usuario.id})">Ver más</button>
      `;
      
      contenedor.appendChild(tarjeta);
    });
    
  } catch (error) {
    contenedor.innerHTML = `<p class="error">Error: ${error.message}</p>`;
    console.error('Error al cargar usuarios:', error);
  }
}

Ejemplo: Buscar Usuario por ID

async function buscarUsuario(id) {
  const resultado = document.getElementById('resultado-busqueda');
  resultado.textContent = 'Buscando...';
  
  try {
    const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
    
    if (response.status === 404) {
      resultado.textContent = 'Usuario no encontrado';
      return;
    }
    
    if (!response.ok) {
      throw new Error(`Error: ${response.status}`);
    }
    
    const usuario = await response.json();
    
    resultado.innerHTML = `
      <h3>${usuario.name}</h3>
      <p>Email: ${usuario.email}</p>
      <p>Usuario: @${usuario.username}</p>
      <p>Sitio web: ${usuario.website}</p>
    `;
    
  } catch (error) {
    resultado.textContent = `Error: ${error.message}`;
  }
}

// Event listener para el formulario de búsqueda
document.getElementById('form-buscar').addEventListener('submit', (e) => {
  e.preventDefault();
  const id = document.getElementById('input-id').value;
  buscarUsuario(id);
});

Ejemplo: Cargar Múltiples Recursos

async function cargarDashboard() {
  const dashboard = document.getElementById('dashboard');
  dashboard.innerHTML = '<p>Cargando dashboard...</p>';
  
  try {
    // Cargar múltiples recursos en paralelo
    const [usuarios, posts, comentarios] = await Promise.all([
      fetch('https://jsonplaceholder.typicode.com/users').then(r => r.json()),
      fetch('https://jsonplaceholder.typicode.com/posts').then(r => r.json()),
      fetch('https://jsonplaceholder.typicode.com/comments').then(r => r.json())
    ]);
    
    dashboard.innerHTML = `
      <div class="stats">
        <div class="stat-card">
          <h3>Usuarios</h3>
          <p class="stat-numero">${usuarios.length}</p>
        </div>
        <div class="stat-card">
          <h3>Posts</h3>
          <p class="stat-numero">${posts.length}</p>
        </div>
        <div class="stat-card">
          <h3>Comentarios</h3>
          <p class="stat-numero">${comentarios.length}</p>
        </div>
      </div>
    `;
    
    console.log('Dashboard cargado exitosamente');
    
  } catch (error) {
    dashboard.innerHTML = `<p class="error">Error al cargar dashboard: ${error.message}</p>`;
  }
}
Usar Promise.all() es mucho más rápido que esperar cada petición secuencialmente.

Ejemplo: Crear Nuevo Recurso (POST)

async function crearPost(titulo, contenido, userId) {
  const resultado = document.getElementById('resultado-post');
  resultado.textContent = 'Creando post...';
  
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        title: titulo,
        body: contenido,
        userId: userId
      })
    });
    
    if (!response.ok) {
      throw new Error(`Error HTTP: ${response.status}`);
    }
    
    const nuevoPost = await response.json();
    
    resultado.innerHTML = `
      <p class="exito">✅ Post creado exitosamente!</p>
      <p>ID: ${nuevoPost.id}</p>
      <p>Título: ${nuevoPost.title}</p>
    `;
    
    console.log('Post creado:', nuevoPost);
    
  } catch (error) {
    resultado.innerHTML = `<p class="error">❌ Error: ${error.message}</p>`;
  }
}

// Formulario para crear post
document.getElementById('form-crear-post').addEventListener('submit', async (e) => {
  e.preventDefault();
  
  const titulo = document.getElementById('titulo').value;
  const contenido = document.getElementById('contenido').value;
  
  await crearPost(titulo, contenido, 1);
  
  // Limpiar formulario
  e.target.reset();
});

Ejemplo: Actualizar Recurso (PUT)

async function actualizarPost(id, datosActualizados) {
  try {
    const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(datosActualizados)
    });
    
    if (!response.ok) {
      throw new Error(`Error: ${response.status}`);
    }
    
    const postActualizado = await response.json();
    console.log('Post actualizado:', postActualizado);
    
    return postActualizado;
    
  } catch (error) {
    console.error('Error al actualizar:', error);
    throw error;
  }
}

// Uso
actualizarPost(1, {
  title: 'Título actualizado',
  body: 'Contenido actualizado',
  userId: 1
}).then(post => {
  console.log('Actualización exitosa:', post);
});

Ejemplo: Eliminar Recurso (DELETE)

async function eliminarPost(id) {
  const confirmacion = confirm(`¿Seguro que quieres eliminar el post ${id}?`);
  
  if (!confirmacion) {
    return;
  }
  
  try {
    const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, {
      method: 'DELETE'
    });
    
    if (!response.ok) {
      throw new Error(`Error: ${response.status}`);
    }
    
    console.log(`Post ${id} eliminado exitosamente`);
    
    // Actualizar UI
    const elemento = document.getElementById(`post-${id}`);
    if (elemento) {
      elemento.remove();
    }
    
  } catch (error) {
    alert(`Error al eliminar: ${error.message}`);
  }
}

Ejemplo: Buscar con Filtros

async function buscarPosts(filtros = {}) {
  const contenedor = document.getElementById('posts-contenedor');
  contenedor.innerHTML = '<p>Buscando...</p>';
  
  try {
    // Construir URL con parámetros
    const params = new URLSearchParams();
    
    if (filtros.userId) {
      params.append('userId', filtros.userId);
    }
    if (filtros.limit) {
      params.append('_limit', filtros.limit);
    }
    
    const url = `https://jsonplaceholder.typicode.com/posts?${params}`;
    const response = await fetch(url);
    
    if (!response.ok) {
      throw new Error(`Error: ${response.status}`);
    }
    
    const posts = await response.json();
    
    if (posts.length === 0) {
      contenedor.innerHTML = '<p>No se encontraron posts</p>';
      return;
    }
    
    contenedor.innerHTML = '';
    
    posts.forEach(post => {
      const div = document.createElement('div');
      div.className = 'post';
      div.innerHTML = `
        <h4>${post.title}</h4>
        <p>${post.body.substring(0, 100)}...</p>
        <small>Usuario ID: ${post.userId}</small>
      `;
      contenedor.appendChild(div);
    });
    
  } catch (error) {
    contenedor.innerHTML = `<p class="error">Error: ${error.message}</p>`;
  }
}

// Uso
buscarPosts({ userId: 1, limit: 5 });

Ejemplo: Manejo Robusto de Errores

async function fetchConManejoCompleto(url) {
  const resultado = document.getElementById('resultado');
  resultado.textContent = 'Cargando...';
  
  try {
    // Agregar timeout
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 5000);
    
    const response = await fetch(url, {
      signal: controller.signal
    });
    
    clearTimeout(timeoutId);
    
    // Verificar tipo de contenido
    const contentType = response.headers.get('Content-Type');
    
    if (!contentType || !contentType.includes('application/json')) {
      throw new Error('La respuesta no es JSON');
    }
    
    // Verificar status
    if (response.status === 404) {
      resultado.textContent = 'Recurso no encontrado';
      return null;
    }
    
    if (response.status === 401) {
      resultado.textContent = 'No autorizado - inicia sesión';
      return null;
    }
    
    if (!response.ok) {
      throw new Error(`Error HTTP: ${response.status}`);
    }
    
    const data = await response.json();
    return data;
    
  } catch (error) {
    if (error.name === 'AbortError') {
      resultado.textContent = 'Timeout: La petición tardó demasiado';
    } else if (error.message.includes('Failed to fetch')) {
      resultado.textContent = 'Error de red: verifica tu conexión';
    } else {
      resultado.textContent = `Error: ${error.message}`;
    }
    
    console.error('Error completo:', error);
    return null;
  }
}

Ejemplo: Caché con localStorage

Combinar Fetch con localStorage para reducir peticiones:
async function obtenerUsuariosConCache() {
  const CACHE_KEY = 'usuarios_cache';
  const CACHE_DURATION = 5 * 60 * 1000; // 5 minutos
  
  // Intentar obtener del cache
  const cached = localStorage.getItem(CACHE_KEY);
  
  if (cached) {
    const { data, timestamp } = JSON.parse(cached);
    const edad = Date.now() - timestamp;
    
    if (edad < CACHE_DURATION) {
      console.log('Usando cache');
      return data;
    }
  }
  
  // Si no hay cache o está expirado, obtener del servidor
  console.log('Obteniendo del servidor');
  const response = await fetch('https://jsonplaceholder.typicode.com/users');
  const data = await response.json();
  
  // Guardar en cache
  localStorage.setItem(CACHE_KEY, JSON.stringify({
    data,
    timestamp: Date.now()
  }));
  
  return data;
}

// Uso
obtenerUsuariosConCache().then(usuarios => {
  console.log('Usuarios:', usuarios);
});
Cuidado con el tamaño de los datos que guardas en localStorage - tiene un límite de ~5-10MB.

Próximos Pasos

Ejemplos de Storage

Aprende a persistir datos en el navegador

Fetch API

Repasa los fundamentos de Fetch

Build docs developers (and LLMs) love