TechLead

APIs Web

Fetch API, Web Storage, IndexedDB, Web Workers y más APIs del navegador

APIs Web Esenciales

Los navegadores proporcionan potentes APIs que permiten a JavaScript interactuar con la red, almacenar datos, ejecutar tareas en segundo plano y acceder a funciones del dispositivo. Comprender estas APIs es esencial para construir aplicaciones web modernas.

APIs Cubiertas

  • Fetch API — Solicitudes HTTP modernas
  • Web Storage — localStorage y sessionStorage
  • IndexedDB — Base de datos del lado del cliente
  • Web Workers — Hilos en segundo plano
  • Intersection Observer — Detección de visibilidad

Fetch API

// Solicitud GET básica
const response = await fetch("/api/users");
const users = await response.json();

// POST con cuerpo JSON
const newUser = await fetch("/api/users", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ name: "Alice", email: "alice@example.com" }),
});

// Ejemplo completo con manejo de errores
async function fetchData(url) {
  try {
    const response = await fetch(url);
    
    if (!response.ok) {
      throw new Error(`Error HTTP: ${response.status}`);
    }
    
    const contentType = response.headers.get("content-type");
    if (contentType?.includes("application/json")) {
      return await response.json();
    }
    return await response.text();
    
  } catch (error) {
    if (error.name === "AbortError") {
      console.log("La solicitud fue cancelada");
    } else {
      console.error("Fetch falló:", error);
    }
    throw error;
  }
}

// Solicitud con timeout usando AbortController
async function fetchWithTimeout(url, timeout = 5000) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeout);
  
  try {
    const response = await fetch(url, { signal: controller.signal });
    return await response.json();
  } finally {
    clearTimeout(timeoutId);
  }
}

Web Storage (localStorage y sessionStorage)

// localStorage - persiste entre sesiones del navegador
localStorage.setItem("theme", "dark");
const theme = localStorage.getItem("theme"); // "dark"
localStorage.removeItem("theme");
localStorage.clear(); // Eliminar todo

// Almacenar objetos (debe convertir a string)
const user = { name: "Alice", preferences: { theme: "dark" } };
localStorage.setItem("user", JSON.stringify(user));
const storedUser = JSON.parse(localStorage.getItem("user"));

// sessionStorage - se borra cuando se cierra la pestaña
sessionStorage.setItem("tempData", "value");

// Wrapper de almacenamiento con expiración
const storage = {
  set(key, value, ttlMs) {
    const item = {
      value,
      expiry: ttlMs ? Date.now() + ttlMs : null,
    };
    localStorage.setItem(key, JSON.stringify(item));
  },
  
  get(key) {
    const itemStr = localStorage.getItem(key);
    if (!itemStr) return null;
    
    const item = JSON.parse(itemStr);
    if (item.expiry && Date.now() > item.expiry) {
      localStorage.removeItem(key);
      return null;
    }
    return item.value;
  }
};

storage.set("cache", data, 3600000); // 1 hora de TTL

IndexedDB

// Abrir base de datos
const request = indexedDB.open("MyDatabase", 1);

request.onerror = () => console.error("Error de BD");

request.onupgradeneeded = (event) => {
  const db = event.target.result;
  
  // Crear almacén de objetos (tabla)
  const store = db.createObjectStore("users", { keyPath: "id" });
  store.createIndex("email", "email", { unique: true });
};

request.onsuccess = (event) => {
  const db = event.target.result;
  
  // Agregar datos
  const transaction = db.transaction(["users"], "readwrite");
  const store = transaction.objectStore("users");
  
  store.add({ id: 1, name: "Alice", email: "alice@example.com" });
  
  // Leer datos
  const getRequest = store.get(1);
  getRequest.onsuccess = () => console.log(getRequest.result);
};

Web Workers

// main.js - Crear y comunicarse con el worker
const worker = new Worker("worker.js");

// Enviar datos al worker
worker.postMessage({ type: "calculate", data: [1, 2, 3, 4, 5] });

// Recibir resultados
worker.onmessage = (event) => {
  console.log("Resultado:", event.data);
};

// worker.js - Hilo en segundo plano
self.onmessage = (event) => {
  const { type, data } = event.data;
  
  if (type === "calculate") {
    // Cálculo pesado sin bloquear el hilo principal
    const result = data.reduce((sum, n) => sum + n, 0);
    self.postMessage(result);
  }
};

Intersection Observer

// Detectar cuándo los elementos entran al viewport
const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add("visible");
      if (entry.target.dataset.src) {
        entry.target.src = entry.target.dataset.src;
        observer.unobserve(entry.target);
      }
    }
  });
}, {
  threshold: 0.1,
  rootMargin: "50px"
});

document.querySelectorAll(".lazy-image").forEach((img) => {
  observer.observe(img);
});

💡 Puntos Clave

  • • Usa Fetch API con async/await para solicitudes de red
  • • localStorage persiste; sessionStorage se borra al cerrar la pestaña
  • • IndexedDB para datos grandes/estructurados del lado del cliente
  • • Web Workers para cálculos pesados sin bloquear la UI
  • • Intersection Observer para carga diferida y efectos de scroll
  • • Siempre maneja errores y verifica el soporte de las APIs