🌐 3.5 · Consumo de APIs: integra mapas, clima, películas, países y más en tu web

⏱ 8h 00min ⚡ 120 XP 🏅 Mid Junior 📖 Frontend Moderno y Proyecto Final
"Las APIs públicas son tus bloques de Lego: combínalas para construir cualquier cosa."

🎯 Objetivo del tema

Al terminar este tema serás capaz de: Al terminar este tema serás capaz de integrar MÚLTIPLES APIs públicas y gratuitas en una sola aplicación React: clima (Open-Meteo), mapas (Leaflet + OpenStreetMap), películas (OMDB), países (REST Countries), Rick and Morty, GitHub, Pokémon, y más. Aprenderás fetch, Axios, manejo de errores, caché, debounce, y patrones profesionales para combinar servicios. El resultado: una app que parece tener miles de datos pero tú solo escribiste unas llamadas fetch.
🎬
Video del instructor

El administrador aún no ha insertado un video para esta sección.

🗺️ Mapa del tema

Una de las cosas más poderosas del frontend moderno es que puedes construir apps INCREÍBLES sin tener tu propio backend: consumiendo APIs públicas y gratuitas. Hay miles: clima, mapas, películas, países, personajes, libros, GitHub, Pokémon, rick and morty, chistes, gatos, espacio... La mayoría no requieren autenticación y devuelven JSON. En este tema vas a aprender a consumir 8+ APIs reales, combinarlas en una app de 'directorio mundial' donde cada país tenga su clima, sus personajes de Rick and Morty, y un mapa de su ubicación. Es el patrón del 80% de las apps modernas.

1. Las 8 APIs públicas gratuitas más útiles para tu web

Catálogo completo: clima, mapas, películas, países, personajes, GitHub, Pokémon, libros.

2. fetch + Async/Await: la base del consumo de APIs

Cómo hacer peticiones HTTP desde el navegador, manejar respuestas, errores, y JSON.

3. Axios: el rey de los clientes HTTP en JS

Alternativa a fetch con mejor DX, interceptores, y cancelación.

4. Patrones profesionales: loading, error, retry, caché

Cómo manejar los 3 estados, reintentar automáticamente, y cachear respuestas.

5. Debounce y throttling: no hacer 1000 fetches

Optimización para búsquedas en vivo: esperar 300ms antes de fetchar.

6. Proyecto integrador: 'Atlas Mundial' con 4 APIs combinadas

Página de país con clima + mapa + personajes de Rick and Morty según localización.

3.5.1 Las 8 APIs públicas gratuitas más útiles para tu web

El ecosistema de APIs públicas es ENORME. Hay miles de servicios que devuelven datos JSON, muchos sin necesitar autenticación. Te presento las 8 que más usarás en tu vida profesional, todas gratis, todas perfectas para aprender y construir proyectos reales. Con solo estas 8 puedes construir apps de prácticamente cualquier categoría.

📷 Imagen referencial: Flujo de consumo de APIs: componente React → useFetch custom hook → fetch/Axios → API pública → JSON → useState → render. Tres estados: loading, error, éxito.
APIURL baseAuthDatosCasos de uso
🌤️ Open-Meteohttps://api.open-meteo.com/v1/forecastNo requiereClima actual y pronóstico (temperatura, lluvia, viento).App del clima, dashboards de viaje, widget del clima.
🗺️ OpenStreetMap (Leaflet)https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.pngNo requiereTiles de mapas del mundo entero.Mapas interactivos, geolocalización, rutas.
🌍 REST Countrieshttps://restcountries.com/v3.1No requiereDatos de 250+ países (capital, población, idiomas, banderas).Enciclopedias, juegos de geografía, dashboards.
🧪 Rick and Mortyhttps://rickandmortyapi.com/apiNo requierePersonajes, episodios, ubicaciones de la serie.Wikis, juegos, dashboards de fans.
🎬 OMDBhttps://www.omdbapi.com/?apikey=YOUR_KEYAPI key (gratis)Películas y series (título, año, poster, rating).Catálogos de películas, reseñas, recomendaciones.
⚡ GitHub APIhttps://api.github.comNo requiere (rate limit)Usuarios, repos, issues, gists, estrellas.Portafolios, dashboards de actividad, estadísticas.
🎮 PokeAPIhttps://pokeapi.co/api/v2No requiereDatos de 1000+ Pokémon (tipos, stats, evoluciones).Pokedex, juegos, calculadoras de daño.
📚 Open Libraryhttps://openlibrary.orgNo requiereDatos de millones de libros (título, autor, portada).Catálogos de bibliotecas, reseñas, recomendaciones.
⭐ Open-Meteo: la mejor API de clima para empezar: Open-Meteo es la MEJOR API de clima para aprender: 100% gratis, sin API key, sin rate limit, y devuelve datos completos (temperatura actual, pronóstico 7 días, lluvia, viento, etc.). Otras como OpenWeatherMap requieren registro y API key, lo cual agrega fricción al aprendizaje. Open-Meteo te permite hacer fetch y obtener el clima de cualquier coordenada en 5 minutos. La regla para elegir API para aprender: que no requiera auth, que tenga documentación clara, y que devuelva JSON simple. Open-Meteo cumple las 3.
🎬
Video del instructor

El administrador aún no ha insertado un video para esta sección.

3.5.2 fetch + Async/Await: la base del consumo de APIs

fetch es la API nativa del navegador para hacer peticiones HTTP. Combinada con async/await, el código se lee como si fuera síncrono pero NO bloquea el hilo. Es el patrón fundamental del 95% de las apps que consumen APIs. En este bloque vas a ver cómo hacer fetch a la Rick and Morty API, REST Countries, y Open-Meteo, con manejo de errores robusto.

El patrón fetch + async/await

// 1. Fetch básico: GET a la Rick and Morty API
async function getPersonajes(page = 1) {
  const response = await fetch(`https://rickandmortyapi.com/api/character?page=${page}`);

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
  }

  const data = await response.json();
  return data.results;  // array de personajes
}

// 2. Fetch con headers y método POST
async function crearUsuario(usuario) {
  const response = await fetch('/api/usuarios', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(usuario),
  });
  if (!response.ok) throw new Error('Error al crear');
  return await response.json();
}

// 3. Fetch con timeout (fetch no tiene timeout nativo)
async function fetchConTimeout(url, ms = 5000) {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), ms);
  try {
    const response = await fetch(url, { signal: controller.signal });
    return await response.json();
  } finally {
    clearTimeout(timeout);
  }
}

// 4. Fetch en paralelo (más rápido)
async function cargarTodo() {
  const [personajes, ubicaciones, episodios] = await Promise.all([
    fetch('https://rickandmortyapi.com/api/character').then(r => r.json()),
    fetch('https://rickandmortyapi.com/api/location').then(r => r.json()),
    fetch('https://rickandmortyapi.com/api/episode').then(r => r.json()),
  ]);
  return { personajes, ubicaciones, episodios };
}

// 5. Fetch con cancelación al desmontar (evita memory leaks)
useEffect(() => {
  const controller = new AbortController();
  fetch(url, { signal: controller.signal })
    .then(r => r.json())
    .then(setData)
    .catch(err => { if (err.name !== 'AbortError') setError(err); });
  return () => controller.abort();  // cleanup
}, [url]);

5 patrones de fetch: básico, POST, timeout, paralelo, cancelación

⭐ fetch no rechaza con 404, tú debes chequearlo: fetch NO rechaza la Promise si la respuesta es 404 o 500. Solo rechaza si hay error de RED (sin internet, DNS, CORS). Por eso SIEMPRE chequea response.ok antes de procesar el body. Si no lo haces, intentas hacer .json() sobre un HTML de error y explota. Es el error #1 de los principiantes con APIs. Regla: if (!response.ok) throw new Error(`HTTP ${response.status}`) SIEMPRE antes del await response.json().
🎬
Video del instructor

El administrador aún no ha insertado un video para esta sección.

3.5.3 Axios: el rey de los clientes HTTP

Axios es una librería que envuelve fetch con una API más amigable: lanza error automáticamente con respuestas 4xx/5xx (a diferencia de fetch), tiene interceptores para añadir tokens automáticamente, soporta cancelación, y tiene mejor manejo de JSON. Es opcional (fetch es suficiente), pero muchos equipos la prefieren por DX. Pesa solo 14KB gzipped, vale la pena.

fetch vs Axios: el mismo trabajo, dos sintaxis

// Instalar: npm install axios
import axios from 'axios';

// 1. GET con fetch (lo que ya conoces):
const response = await fetch('https://rickandmortyapi.com/api/character');
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();

// 2. GET con Axios (más conciso, lanza error automáticamente):
const { data } = await axios.get('https://rickandmortyapi.com/api/character');
// data.results es el array de personajes

// 3. POST con Axios:
const { data: nuevoUsuario } = await axios.post('/api/usuarios', { nombre, email });

// 4. Interceptores: añade el token JWT automáticamente a TODAS las peticiones
const api = axios.create({ baseURL: 'https://mi-app.com/api' });
api.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) config.headers.Authorization = `Bearer ${token}`;
  return config;
});

// 5. Interceptores de respuesta: maneja errores globalmente
api.interceptors.response.use(
  response => response,
  error => {
    if (error.response?.status === 401) {
      localStorage.removeItem('token');
      window.location.href = '/login';  // sesión expirada
    }
    return Promise.reject(error);
  }
);

// 6. Cancelación: equivalente a AbortController de fetch
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000);
try {
  const { data } = await axios.get('/api/lenta', { signal: controller.signal });
} catch (err) {
  if (axios.isCancel(err)) console.log('Petición cancelada');
}

Axios: la alternativa a fetch con mejor DX

⭐ Interceptores de Axios: tu mejor amigo con auth: Los interceptores de Axios son la MEJOR feature: defines el token JWT una vez, y TODAS las peticiones lo llevan automáticamente. Si el backend devuelve 401 (token expirado), el interceptor puede hacer logout automático. Es el patrón que usan apps como Gmail, Notion, Slack. Si tu app tiene auth, usa una instancia de Axios con interceptores configurados; nunca añadas el token manualmente en cada petición. Es menos código, menos bugs, y más mantenible.
🎬
Video del instructor

El administrador aún no ha insertado un video para esta sección.

3.5.4 Patrones profesionales: loading, error, retry, caché

Hacer fetch es solo el 20% del trabajo. El otro 80% es manejar los 3 estados (cargando, error, éxito), reintentar automáticamente cuando falla, y cachear respuestas para no hacer la misma petición 1000 veces. Estos patrones son los que separan una app amateur de una profesional. Vamos a verlos con un custom hook que combina todo.

useFetch profesional: con retry, caché, y cancelación

import { useState, useEffect, useRef } from 'react';

// Custom hook profesional: useFetch con retry, caché y cancelación
function useFetchAvanzado(url, opciones = {}) {
  const {
    reintentos = 3,           // número de reintentos automáticos
    delayReintento = 1000,    // ms entre reintentos
    cacheMs = 5 * 60 * 1000,  // caché de 5 minutos por default
  } = opciones;

  const [data, setData] = useState(null);
  const [cargando, setCargando] = useState(true);
  const [error, setError] = useState(null);
  const cacheRef = useRef(new Map());

  useEffect(() => {
    if (!url) return;

    const controller = new AbortController();
    let intentos = 0;

    async function fetchConReintentos() {
      // 1. Revisar caché
      const cached = cacheRef.current.get(url);
      if (cached && Date.now() - cached.timestamp < cacheMs) {
        setData(cached.data);
        setCargando(false);
        return;
      }

      try {
        const response = await fetch(url, { signal: controller.signal });
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        const result = await response.json();

        // Guardar en caché
        cacheRef.current.set(url, { data: result, timestamp: Date.now() });
        setData(result);
        setError(null);
      } catch (err) {
        if (err.name === 'AbortError') return;
        if (intentos < reintentos) {
          intentos++;
          setTimeout(fetchConReintentos, delayReintento);
        } else {
          setError(err.message);
        }
      } finally {
        if (intentos === 0) setCargando(false);
        if (intentos >= reintentos) setCargando(false);
      }
    }

    setCargando(true);
    fetchConReintentos();
    return () => controller.abort();
  }, [url]);

  return { data, cargando, error };
}

// Uso:
function DetallePais({ id }) {
  const { data, cargando, error } = useFetchAvanzado(
    `https://restcountries.com/v3.1/alpha/${id}`,
    { reintentos: 3, cacheMs: 10 * 60 * 1000 }  // 10 min de caché
  );
  if (cargando) return <p>Cargando...</p>;
  if (error) return <p>Error: {error}</p>;
  return <h1>{data[0].name.common}</h1>;
}

useFetchAvanzado: con retry, caché, y cancelación

⭐ Caché en frontend: ahorra fetches y dinero: El caché en el frontend es FUNDAMENTAL para apps con datos que no cambian cada segundo: lista de países (cambia 1 vez al año), personajes de Rick and Morty (nunca), películas (rara vez). Con un caché de 5-10 minutos en localStorage o memoria, reduces fetches a la API en un 90%. Para datos que cambian frecuentemente (clima cada 10 min, noticias cada hora), el caché debe ser corto. Para datos estáticos (lista de países), puede ser de horas. Es la diferencia entre hacer 1 petición y 1000 a la misma API.
🎬
Video del instructor

El administrador aún no ha insertado un video para esta sección.

3.5.5 Debounce y throttling: optimizando búsquedas en vivo

Si el usuario escribe 'argentina' en un campo de búsqueda, y cada tecla hace un fetch a la API, son 9 peticiones innecesarias. La solución: DEBOUNCE, que espera 300ms después de la última tecla antes de hacer el fetch. Es una de las optimizaciones más importantes y más simples. Vamos a implementarlo con un custom hook.

useDebounce: el hook que TODA búsqueda en vivo necesita

import { useState, useEffect } from 'react';

// Hook: devuelve el valor solo después de 'delay' ms sin cambios
function useDebounce(valor, delay = 300) {
  const [debounced, setDebounced] = useState(valor);

  useEffect(() => {
    const timer = setTimeout(() => setDebounced(valor), delay);
    return () => clearTimeout(timer);  // cancela si el valor cambia antes del delay
  }, [valor, delay]);

  return debounced;
}

// Uso: buscador de países en vivo con debounce
function BuscadorPaises() {
  const [termino, setTermino] = useState('');
  const terminoDebounced = useDebounce(termino, 300);  // espera 300ms
  const { data, cargando } = useFetch(
    terminoDebounced ? `https://restcountries.com/v3.1/name/${terminoDebounced}` : null
  );

  return (
    <div>
      <input
        value={termino}
        onChange={e => setTermino(e.target.value)}
        placeholder="Buscar país..."
      />
      {cargando && <p>Buscando...</p>}
      {data && data.map(p => <p key={p.cca3}>{p.name.common}</p>)}
    </div>
  );
}

// SIN debounce: 9 fetches por escribir 'argentina'
// CON debounce: 1 fetch (después de 300ms de inactividad)

useDebounce: optimiza búsquedas en vivo

⭐ 300ms es la magia del debounce: El 300ms de delay en debounce es la REGLA DE ORO de UX: suficientemente corto para que el usuario sienta que la búsqueda es 'instantánea', suficientemente largo para no hacer 10 fetches. Si la API responde en <100ms (como Rick and Morty, REST Countries), puedes usar 200ms. Si responde en 500ms+ (como OMDB), usa 400-500ms. La regla: delay = tiempo de respuesta típico de la API. Es una optimización TAN simple que no hacerla es negligencia profesional.
🎬
Video del instructor

El administrador aún no ha insertado un video para esta sección.

3.5.6 Proyecto integrador: 'Atlas Mundial' combinando 4 APIs

El proyecto final del tema: 'Atlas Mundial', una SPA que combina 4 APIs públicas en una sola experiencia. Al hacer clic en un país, verás: su bandera y datos básicos (REST Countries), su clima actual (Open-Meteo), un mapa interactivo con su ubicación (Leaflet + OpenStreetMap), y un personaje de Rick and Morty asignado aleatoriamente (Rick and Morty API). Es el patrón del 80% de las apps modernas: combinar servicios para crear una experiencia unificada.

El componente que combina 4 APIs

import { useState, useEffect } from 'react';
import { useParams, useNavigate, Link } from 'react-router-dom';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';

// Custom hook: useFetch (del Tema 3.2)
function useFetch(url) {
  const [data, setData] = useState(null);
  const [cargando, setCargando] = useState(true);
  const [error, setError] = useState(null);
  useEffect(() => {
    if (!url) return;
    const controller = new AbortController();
    setCargando(true);
    fetch(url, { signal: controller.signal })
      .then(r => r.ok ? r.json() : Promise.reject(new Error(`HTTP ${r.status}`)))
      .then(setData)
      .catch(err => { if (err.name !== 'AbortError') setError(err.message); })
      .finally(() => setCargando(false));
    return () => controller.abort();
  }, [url]);
  return { data, cargando, error };
}

// Hook: usePersonajeRickMortyAleatorio
function usePersonajeAleatorio() {
  const { data } = useFetch('https://rickandmortyapi.com/api/character/' + Math.floor(Math.random() * 826));
  return data;
}

// Página principal: combina 4 APIs
function AtlasMundial() {
  const { id } = useParams();  // código cca3 del país

  // 1. REST Countries: datos del país
  const { data: paises } = useFetch(
    `https://restcountries.com/v3.1/alpha/${id}?fields=name,capital,population,flag,languages,latlng,region`
  );

  // 2. Open-Meteo: clima actual del país
  const pais = paises?.[0];
  const { data: clima } = useFetch(
    pais ? `https://api.open-meteo.com/v1/forecast?latitude=${pais.latlng[0]}&longitude=${pais.latlng[1]}&current_weather=true` : null
  );

  // 3. Rick and Morty: personaje aleatorio (para entretenimiento)
  const personaje = usePersonajeAleatorio();

  if (!pais) return <p>Cargando país...</p>;

  return (
    <article className="atlas-pais">
      <header>
        <img src={pais.flag} alt={pais.name.common} className="flag-grande" />
        <h1>{pais.name.common}</h1>
        <p><strong>Capital:</strong> {pais.capital?.[0]}</p>
        <p><strong>Población:</strong> {pais.population.toLocaleString()}</p>
        <p><strong>Idiomas:</strong> {Object.values(pais.languages || {}).join(', ')}</p>
      </header>

      <section className="seccion-clima">
        <h2>🌤️ Clima actual</h2>
        {clima ? (
          <div>
            <p className="temperatura">{clima.current_weather.temperature}°C</p>
            <p>Viento: {clima.current_weather.windspeed} km/h</p>
            <p>Hora: {new Date(clima.current_weather.time).toLocaleString()}</p>
          </div>
        ) : <p>Cargando clima...</p>}
      </section>

      <section className="seccion-mapa">
        <h2>🗺️ Ubicación</h2>
        <MapContainer center={pais.latlng} zoom={4} style={{height: '300px'}}>
          <TileLayer
            url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
            attribution='© OpenStreetMap contributors'
          />
          <Marker position={pais.latlng}>
            <Popup>{pais.name.common}</Popup>
          </Marker>
        </MapContainer>
      </section>

      <section className="seccion-personaje">
        <h2>🧪 Personaje aleatorio de Rick and Morty</h2>
        {personaje ? (
          <div>
            <img src={personaje.image} alt={personaje.name} />
            <p><strong>{personaje.name}</strong> ({personaje.species})</p>
            <p>Estado: {personaje.status}</p>
          </div>
        ) : <p>Cargando personaje...</p>}
      </section>
    </article>
  );
}

// Para usar el mapa, instala:
// npm install react-leaflet leaflet

Atlas Mundial: 4 APIs combinadas en una sola página

⭐ Combinar APIs: el patrón del 80% de las apps modernas: Este proyecto muestra el patrón del 80% de las apps modernas: combinar APIs públicas para crear una experiencia unificada. Slack combina Slack + Google Drive + GitHub. Notion combina editor + bases de datos + calendario. Spotify combina música + podcasts + letras + recomendaciones. Ninguna de estas apps tiene su propia música, ni su propio calendario, ni su propio editor: COMBINAN servicios. Tú acabas de hacer lo mismo con REST Countries + Open-Meteo + Leaflet + Rick and Morty. Es el patrón. Una vez que lo entiendes, puedes construir cualquier app: combina 3-4 APIs públicas y tendrás un producto competitivo en semanas, no meses.
🎬
Video del instructor

El administrador aún no ha insertado un video para esta sección.

📚 Contenido ampliado

Material adicional, referencias externas verificadas y ejemplos extendidos.

🤖 AI Mission

Las APIs públicas son tus bloques de Lego: combínalas para crear cualquier cosa.

Misión: Crea una app propia que combine al menos 4 APIs públicas de las 8 que vimos. Por ejemplo: una app de 'Viaje Perfecto' que combine REST Countries (destino) + Open-Meteo (clima) + Leaflet (mapa) + OMDB (películas filmas en ese país). Pídele a la IA que sugiera 2 combinaciones creativas. Anota: ¿cuál API te sorprendió más? ¿cuál fue más fácil de integrar?

Pasos sugeridos

  1. Elige una idea de app que combine 4 APIs (ej: 'Viaje Perfecto', 'Atlas Mundial', 'Cine por país', 'Pokédex geográfico').
  2. Diseña el layout: qué página muestra qué datos, cómo se navega entre secciones.
  3. Setup: Vite + React + react-router-dom + react-leaflet + axios (opcional).
  4. Crea components/ para cada componente: PaisCard, ClimaCard, Mapa, PeliculasList, etc.
  5. Crea hooks/useFetch.js (con caché y cancelación), hooks/useDebounce.js (para búsquedas).
  6. Integra las APIs en pages/: una página por cada 'sección' (país, clima, mapa, películas).
  7. Maneja los 3 estados en cada sección: loading (skeleton), error (mensaje + retry), éxito (datos).
  8. Estiliza con Tailwind o CSS Modules. Tema oscuro.
  9. Prueba: verifica que todas las APIs cargan, que los errores se manejan, que el caché funciona.
  10. Pídele a la IA: 'Tengo esta app con 4 APIs. Sugiere 2 mejoras: features (búsqueda, favoritos, comparador) o robustez (error boundaries, suspense). NO me des código, dime QUÉ agregar y por qué.'
  11. Aplica 2 mejoras.
  12. Sube a Vercel. Comparte URL.
  13. Anota: 3 APIs que aprendiste, 1 que te sorprendió, y qué combinación creativa podrías hacer con otras 4.

📓 Entregable: URL pública de tu app con 4 APIs combinadas, capturas de: cada API cargando datos, manejo de errores, caché funcionando, código de los hooks personalizados, y media página de cuaderno con tu combinación favorita.

🚫 Errores típicos de razonamiento

Error 1: Hacer fetch sin chequear response.ok.
Por qué: fetch NO rechaza la Promise con 404 o 500. Solo rechaza con errores de red. Si no chequeas response.ok, intentas hacer .json() sobre un HTML de error y explota. Regla: SIEMPRE if (!response.ok) throw new Error(`HTTP ${response.status}`) ANTES del await response.json(). Es el error #1 con APIs.
Error 2: Hacer un fetch por cada tecla del usuario en una búsqueda.
Por qué: Si el usuario escribe 'argentina' (9 letras), son 9 peticiones innecesarias a la API. Usa DEBOUNCE: espera 300ms después de la última tecla antes de hacer el fetch. Reduce 90% de las peticiones, sin perder UX. Es la optimización más simple y con mayor impacto en apps con búsqueda en vivo.
Error 3: No limpiar el fetch al desmontar el componente (memory leak).
Por qué: Si el usuario navega a otra página antes de que el fetch termine, React intentará hacer setState en un componente desmontado, mostrando el warning 'Can't perform a React state update on an unmounted component'. Usa SIEMPRE AbortController: return () => controller.abort() en el cleanup del useEffect. Es una línea que evita bugs difíciles de detectar.
Error 4: Hardcodear API keys en el código.
Por qué: Si tu código se sube a Git con la API key hardcodeada, cualquiera puede usarla (y abusar de ella, haciéndote gastar tu cuota). SIEMPRE variables de entorno: const API_KEY = import.meta.env.VITE_OMDB_KEY. Y .env en .gitignore. Es un riesgo de seguridad Y financiero si tienes una API de pago.
Error 5: Mostrar la API key al usuario (en DevTools o en el bundle).
Por qué: Si tu variable empieza con VITE_, se incluye en el bundle FINAL que se envía al navegador. Si usas import.meta.env.VITE_OMDB_KEY, el usuario puede verla en DevTools > Sources. Para keys SECRETAS (que NO deben verse), usa REACT_APP_ sin el prefijo, o mejor: haz las llamadas con keys en el BACKEND. La regla: keys públicas (deben verse, ej: anon de Supabase): OK en frontend. Keys secretas (nunca deben verse): backend SIEMPRE.

🧪 Laboratorio práctico

Las APIs públicas son tus bloques de Lego. Combínalas para construir cualquier cosa.

Laboratorio: Atlas Mundial: SPA con 4 APIs públicas combinadas

Objetivo: Construir una SPA completa que combine 4 APIs públicas gratuitas en una sola experiencia coherente: REST Countries (datos del país), Open-Meteo (clima actual), Leaflet + OpenStreetMap (mapa interactivo), y Rick and Morty (personaje aleatorio). Practicarás: fetch con caché, useDebounce, manejo de errores, y composición de APIs.

Pasos

  1. Crea el proyecto: npm create vite@latest atlas-mundial -- --template react.
  2. Instala: npm install react-router-dom react-leaflet leaflet axios.
  3. Importa los CSS de Leaflet en main.jsx: import 'leaflet/dist/leaflet.css';.
  4. Crea hooks/useFetch.js (con caché, retry, cancelación).
  5. Crea hooks/useDebounce.js (para búsquedas en vivo).
  6. Crea components/Map.jsx: MapContainer + TileLayer + Marker con latlng del país.
  7. Crea components/ClimaCard.jsx: usa Open-Meteo con las coordenadas del país.
  8. Crea components/PersonajeAleatorio.jsx: Rick and Morty API con ID aleatorio.
  9. Crea pages/Inicio.jsx: lista de países con búsqueda en vivo (useDebounce).
  10. Crea pages/DetallePais.jsx: integra los 4 componentes (datos, clima, mapa, personaje).
  11. App.jsx: BrowserRouter con rutas /, /pais/:id, /sobre, *.
  12. Maneja los 3 estados en cada componente: loading (skeleton), error (mensaje + retry), éxito.
  13. Estiliza con Tailwind o CSS Modules. Tema oscuro con custom properties.
  14. Prueba: Argentina → ver bandera, clima (-34.6, -58.4), mapa centrado en Buenos Aires, personaje aleatorio de Rick and Morty.
  15. Sube a Vercel. Comparte URL.
  16. Haz commit: 'feat: Atlas Mundial con 4 APIs públicas integradas'.

📓 Entregable: URL pública de Atlas Mundial, capturas de: país con todos los datos, clima actual, mapa interactivo, personaje de Rick and Morty, código de los hooks personalizados, y commit en Git.

📓 Tu cuaderno: Anota pseudocódigo, diagramas, errores que encontraste y respuestas a "explica sin código". La escritura manual refuerza tu razonamiento. Tu profesor puede pedirte que subas fotos de páginas específicas.