¿Has visto páginas que muestran el clima actual, noticias en tiempo real, o datos de redes sociales? Eso se hace con APIs. Y si ya sabes manipular el DOM, estás listo para el siguiente nivel: traer datos de internet.
En este tutorial vas a aprender a conectar tu página web con servicios reales y crear aplicaciones que realmente impresionan. Sin trucos, sin complicaciones técnicas.
🤔 ¿Qué es una API (explicado sin tecnicismos)?
Una API es como un camarero en un restaurante. Tú (tu página web) le pides algo al camarero (la API), y él va a la cocina (el servidor) y te trae lo que pediste.
Restaurante normal
Tú: "Quiero una pizza"
Camarero: Va a la cocina
Resultado: Te trae tu pizza
API en internet
Tu página: "Quiero el clima de Madrid"
API: Va al servidor de clima
Resultado: Te trae "22°C, soleado"
🎯 ¿Por qué necesitas APIs?
📊 Datos reales
En lugar de mostrar "22°C" hardcodeado, muestras la temperatura REAL de ahora mismo.
🔄 Contenido dinámico
Tu página se actualiza sola. Como Instagram que carga nuevas fotos sin que recargues.
🚀 Apps profesionales
La diferencia entre una página de práctica y una aplicación real que la gente usaría.
🛠️ fetch(): Tu nueva herramienta mágica
fetch() es la función de JavaScript que te permite "ir a buscar" datos de internet. Es como un mensajero súper rápido que trae lo que le pidas.
🏗️ Estructura básica (sin miedo):
fetch('https://api-example.com/datos')
.then(response => response.json())
.then(data => {
// Aquí ya tienes los datos para usar
console.log(data);
});
📋 Paso a paso:
fetch('url')
Le dices a JavaScript: "ve a esta dirección y trae lo que hay"
.then(response => response.json())
Conviertes la respuesta en datos que JavaScript entiende
.then(data => { ... })
¡Ya puedes usar los datos! Los muestras, los guardas, lo que quieras
🚀 Proyecto práctico: Ejemplos que funcionan
Vamos con ejemplos reales. Cada uno más interesante que el anterior.
📝 Nivel 1: Tu primera llamada API
Empezamos con algo simple: traer una cita inspiracional random.
🎮 Demo interactivo:
Haz clic para obtener una cita inspiracional
👆 Datos reales desde internet
🛡️ SEGURIDAD IMPORTANTE: ¡No uses innerHTML con APIs!
El código de abajo tiene una vulnerabilidad XSS grave. Lo mostramos para que aprendas qué NO hacer:
display.innerHTML = `<p>"${data.text}"</p>`; // ⚠️ XSS vulnerable!
¿Por qué es peligroso? Si la API devuelve algo como <script>alert('hack')</script>, se ejecutará en tu página.
JavaScript SEGURO (úsalo así):
// 1. Encontrar elementos
const boton = document.querySelector('#quote-btn');
const display = document.querySelector('#quote-display');
// 2. Función para obtener cita
async function obtenerCita() {
try {
const response = await fetch('https://thequoteshub.com/api/');
const data = await response.json();
// 3. Crear elementos DOM de forma SEGURA
display.replaceChildren(); // Limpiar
const quoteText = document.createElement('p');
quoteText.className = 'quote-text';
quoteText.textContent = `"${data.text}"`;
const quoteAuthor = document.createElement('p');
quoteAuthor.className = 'quote-author';
quoteAuthor.textContent = `- ${data.author}`;
// 4. Agregar al DOM
display.appendChild(quoteText);
display.appendChild(quoteAuthor);
} catch (error) {
display.replaceChildren();
const errorMsg = document.createElement('p');
errorMsg.textContent = 'Error: No se pudo obtener la cita';
display.appendChild(errorMsg);
}
}
// 5. Escuchar el click
boton.addEventListener('click', obtenerCita);
🤓 ¿Por qué este código es SEGURO?
- createElement(): Crea elementos HTML de forma segura
- textContent: Inserta SOLO texto, nunca ejecuta HTML/JS
- replaceChildren(): Limpia el contenedor sin vulnerabilidades
- appendChild(): Agrega elementos de forma controlada
Resultado: Aunque la API devuelva <script>, se mostrará como texto inofensivo.
⚠️ ¡MUY IMPORTANTE! Las claves del JSON
Cada API devuelve un JSON con claves específicas. Si usas la clave equivocada, obtienes undefined.
{
"text": "La vida es lo que pasa...",
"author": "John Lennon",
"id": 12345
}
data.text // ✅ Correcto
data.author // ✅ Correcto
data.content // ❌ undefined!
data.quote // ❌ undefined!
Regla de oro: Siempre revisa la documentación de la API o prueba la respuesta con console.log(data) primero.
🔍 DOMINAR DATOS JSON COMPLEJOS: Acceso a arrays y objetos anidados
El reto real de las APIs: Las respuestas no son simples. Son estructuras complejas con arrays dentro de objetos, objetos dentro de arrays, y múltiples niveles de anidación.
💪 Lo que vas a dominar aquí:
- Acceder a propiedades dentro de objetos anidados
- Navegar arrays que contienen objetos
- Manejar estructuras de datos de múltiples niveles
- Evitar errores de "undefined" al acceder a propiedades
📦 Anatomía de una respuesta JSON real
Ejemplo de respuesta típica de una API de clima:
{
"coord": {
"lon": -3.7038,
"lat": 40.4168
},
"weather": [
{
"id": 800,
"main": "Clear",
"description": "cielo claro",
"icon": "01d"
}
],
"main": {
"temp": 22.5,
"feels_like": 21.8,
"humidity": 45
},
"sys": {
"country": "ES",
"sunrise": 1692772892
},
"name": "Madrid"
}
🎯 PASO A PASO: Cómo acceder a cada propiedad
Propiedades simples (nivel 1)
data.name // "Madrid"
Regla: Usa el punto (.) para acceder a propiedades directas
Objetos anidados (nivel 2)
data.coord.lon // -3.7038
data.coord.lat // 40.4168
data.main.temp // 22.5
data.sys.country // "ES"
Regla: Encadena puntos para "navegar" dentro de objetos
Arrays con objetos (nivel 2+)
data.weather // El array completo
data.weather[0] // El primer elemento del array
data.weather[0].main // "Clear"
data.weather[0].description // "cielo claro"
Regla: Usa [0] para acceder al primer elemento, [1] al segundo, etc.
⚠️ ERRORES COMUNES y cómo evitarlos
Error #1: Array vacío
data.weather[0].main // Error si weather está vacío!
// Verificar si existe y tiene elementos
if (data.weather && data.weather.length > 0) {
const weatherMain = data.weather[0].main;
}
// O usando operador opcional (?)
const weatherMain = data.weather?.[0]?.main || 'No disponible';
Error #2: Propiedad inexistente
data.main.pressure // undefined si no existe
// Verificar antes de usar
const pressure = data.main.pressure || 'No disponible';
// O con operador opcional
const pressure = data.main?.pressure ?? 'No disponible';
🎮 PRÁCTICA: Ejemplo con GitHub API (datos reales y complejos)
La API de GitHub devuelve datos MUY anidados. Perfecto para practicar:
{
"login": "octocat",
"id": 1,
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"name": "The Octocat",
"company": "@github",
"public_repos": 8,
"followers": 4008,
"following": 9,
"created_at": "2011-01-25T18:44:36Z",
"plan": {
"name": "pro",
"space": 976562499,
"private_repos": 9999
}
}
📝 Cómo extraer cada dato:
// ✅ Datos simples
const username = data.login; // "octocat"
const realName = data.name; // "The Octocat"
const followers = data.followers; // 4008
// ✅ Objeto anidado
const planName = data.plan.name; // "pro"
const planSpace = data.plan.space; // 976562499
// ✅ Acceso SEGURO (por si plan no existe)
const safePlanName = data.plan?.name || 'No tiene plan';
🚀 CASO PRÁCTICO: API con arrays de objetos complejos
Ejemplo real de una API de posts con comentarios:
{
"posts": [
{
"id": 1,
"title": "Mi primer post",
"author": {
"name": "Juan Pérez",
"avatar": "https://example.com/juan.jpg",
"social": {
"twitter": "@juanperez",
"github": "juanperez"
}
},
"comments": [
{
"id": 101,
"text": "¡Excelente post!",
"author": {
"name": "María García"
}
},
{
"id": 102,
"text": "Muy útil, gracias",
"author": {
"name": "Carlos López"
}
}
]
}
]
}
🎯 ACCESO PASO A PASO a esta estructura compleja:
// 1. ✅ Obtener el primer post
const firstPost = data.posts[0];
// 2. ✅ Datos del post
const postTitle = firstPost.title; // "Mi primer post"
// 3. ✅ Autor del post (objeto anidado)
const authorName = firstPost.author.name; // "Juan Pérez"
// 4. ✅ Social del autor (objeto anidado dentro de otro objeto)
const authorTwitter = firstPost.author.social.twitter; // "@juanperez"
// 5. ✅ Primer comentario
const firstComment = firstPost.comments[0];
const commentText = firstComment.text; // "¡Excelente post!"
// 6. ✅ Autor del comentario
const commentAuthor = firstComment.author.name; // "María García"
🔄 Recorrer TODOS los comentarios (bucles con datos anidados):
// ✅ Mostrar todos los comentarios del primer post
firstPost.comments.forEach((comment, index) => {
console.log(`Comentario ${index + 1}:`);
console.log(`- ${comment.text}`);
console.log(`- Por: ${comment.author.name}`);
});
// ✅ Versión SEGURA (por si no hay comentarios)
if (firstPost.comments && firstPost.comments.length > 0) {
firstPost.comments.forEach(comment => {
// Procesar comentarios de forma segura
const authorName = comment.author?.name || 'Anónimo';
console.log(`${comment.text} - ${authorName}`);
});
} else {
console.log('No hay comentarios en este post');
}
🎯 REGLAS DE ORO para datos JSON complejos:
- console.log(data) primero: Siempre imprime la respuesta completa para ver su estructura
- Navega paso a paso: Ve de lo general (data) a lo específico (data.posts[0].author.name)
- Verifica antes de acceder: Usa && para verificar que exista, o ?. para acceso opcional
- Siempre maneja arrays vacíos: Verifica .length > 0 antes de usar [0]
- Usa valores por defecto: Operador || para tener fallbacks como 'No disponible'
💡 Herramientas para debuggear JSON complejo
🔍 En el navegador:
// Inspeccionar estructura completa
console.log('API Response:', data);
// Ver claves disponibles
console.log('Keys:', Object.keys(data));
// Verificar tipo
console.log('Es array?', Array.isArray(data));
🛠️ Chrome DevTools:
- F12 → Console
- Network → XHR para ver llamadas
- Click en respuesta → Preview tab
- Expandir objetos para navegar
📝 Nivel 2: Buscador de usuarios de GitHub
Ahora algo más útil: buscar perfiles reales de GitHub.
🎮 Demo interactivo:
Busca cualquier usuario de GitHub (ej: octocat, torvalds, gaearon)
HTML necesario:
<input type="text" id="github-input" placeholder="Usuario de GitHub...">
<button id="github-btn">Buscar</button>
<div id="github-result"></div>
🛡️ Ejemplo GitHub: Versión SEGURA
El siguiente código muestra la forma segura de crear elementos dinámicos:
JavaScript SEGURO para GitHub:
const input = document.querySelector('#github-input');
const boton = document.querySelector('#github-btn');
const resultado = document.querySelector('#github-result');
async function buscarUsuario() {
const username = input.value.trim();
// Validación SEGURA
if (username === '') {
resultado.replaceChildren();
const errorMsg = document.createElement('p');
errorMsg.textContent = 'Por favor escribe un nombre de usuario';
resultado.appendChild(errorMsg);
return;
}
// Loading SEGURO
resultado.replaceChildren();
const loading = document.createElement('p');
loading.textContent = 'Buscando...';
resultado.appendChild(loading);
try {
const response = await fetch(`https://api.github.com/users/${username}`);
if (!response.ok) {
throw new Error('Usuario no encontrado');
}
const data = await response.json();
// Crear tarjeta SEGURA
resultado.replaceChildren();
const userCard = document.createElement('div');
userCard.className = 'user-card';
const avatar = document.createElement('img');
avatar.src = data.avatar_url; // Seguro: propiedad directa
avatar.alt = 'Avatar';
avatar.className = 'avatar';
const name = document.createElement('h3');
name.textContent = data.name || data.login; // Seguro: solo texto
userCard.appendChild(avatar);
userCard.appendChild(name);
// ... más elementos
resultado.appendChild(userCard);
} catch (error) {
resultado.replaceChildren();
const errorMsg = document.createElement('p');
errorMsg.textContent = `❌ ${error.message}`;
resultado.appendChild(errorMsg);
}
}
boton.addEventListener('click', buscarUsuario);
📝 Nivel 3: App del clima (como los profesionales)
El clásico: una app que muestra el clima actual de cualquier ciudad.
🎮 Demo interactivo:
Prueba con: Madrid, Barcelona, Buenos Aires, Ciudad de México
🛡️ Clima SEGURO: Sin innerHTML
Ejemplo profesional usando createElement() para máxima seguridad:
JavaScript SEGURO del clima:
// API key gratuita (en producción, mantén esto secreto)
const API_KEY = 'TU_API_KEY_AQUI';
const BASE_URL = 'https://api.openweathermap.org/data/2.5/weather';
async function obtenerClima() {
const ciudad = document.querySelector('#city-input').value.trim();
const resultado = document.querySelector('#weather-result');
if (ciudad === '') return;
// Loading SEGURO
resultado.replaceChildren();
const loading = document.createElement('p');
loading.textContent = 'Obteniendo clima...';
resultado.appendChild(loading);
try {
const url = `${BASE_URL}?q=${ciudad}&appid=${API_KEY}&units=metric&lang=es`;
const response = await fetch(url);
if (!response.ok) {
throw new Error('Ciudad no encontrada');
}
const data = await response.json();
// Crear weather card de forma SEGURA
resultado.replaceChildren();
const weatherCard = document.createElement('div');
weatherCard.className = 'weather-card';
const title = document.createElement('h3');
title.textContent = `🌍 ${data.name}, ${data.sys.country}`;
const temperature = document.createElement('div');
temperature.className = 'temperature';
temperature.textContent = `${Math.round(data.main.temp)}°C`;
const description = document.createElement('p');
description.className = 'description';
description.textContent = data.weather[0].description;
// Ensamblar elementos
weatherCard.appendChild(title);
weatherCard.appendChild(temperature);
weatherCard.appendChild(description);
resultado.appendChild(weatherCard);
} catch (error) {
resultado.replaceChildren();
const errorMsg = document.createElement('p');
errorMsg.textContent = `❌ ${error.message}`;
resultado.appendChild(errorMsg);
}
}
⚠️ Errores comunes con APIs (y cómo solucionarlos)
🎯 Resumen de seguridad:
En este tutorial has aprendido a usar métodos DOM seguros:
- ✅ createElement() en lugar de innerHTML
- ✅ textContent en lugar de insertar HTML
- ✅ replaceChildren() para limpiar contenedores
- ✅ appendChild() para construir elementos
Resultado: Código 100% seguro contra XSS 🛡️
Error #1: No manejar errores
fetch(url).then(data => {
// ¿Y si falla?
});
fetch(url)
.then(data => {/* éxito */})
.catch(error => {/* error */});
Por qué: Las APIs pueden fallar. Internet puede fallar. Siempre maneja errores.
Error #2: No verificar response.ok
fetch(url)
.then(response => response.json()) // ¡Error 404!
fetch(url)
.then(response => {
if (!response.ok) throw new Error('Error HTTP');
return response.json();
})
Por qué: fetch() no rechaza automáticamente códigos 404, 500, etc.
Error #3: API keys expuestas
// En el frontend, visible para todos
const apiKey = 'sk-1234567890abcdef';
// Usar APIs públicas sin key, o
// Hacer llamadas desde tu backend
Por qué: Cualquiera puede ver tu API key y usarla (y cobrarte).
Error #4: No mostrar estados de carga
// Usuario hace click... ¿pasa algo?
fetch(url).then(...);
button.textContent = 'Cargando...';
fetch(url).then(...);
Por qué: Las APIs pueden tardar. El usuario necesita feedback visual.
🎯 APIs gratis para principiantes
Estas APIs no requieren registro y puedes usarlas ahora mismo:
🎭 Random Quotes
https://thequoteshub.com/api/
Citas célebres aleatorias. La que usamos en este tutorial.
🐕 Dog Photos
https://dog.ceo/api/breeds/image/random
Fotos random de perritos. ✅ Funciona perfectamente.
📊 Datos de Prueba
https://httpbin.org/uuid
Genera UUID únicos. Perfecta para IDs aleatorios.
😂 Chuck Norris Facts
https://api.chucknorris.io/jokes/random
Chistes de Chuck Norris. ✅ Funciona y garantiza risas.
🏛️ GitHub API
https://api.github.com/users/octocat
Datos públicos de usuarios. ✅ Perfecta para portfolios.
🌐 APIs Públicas
https://httpbin.org/json
Datos JSON de prueba. Siempre disponible para testing.
🚀 Proyecto completo: Explorador de APIs
Vamos a crear una mini-aplicación que combine múltiples APIs:
🎮 Demo final - Mini Dashboard:
Haz clic en cualquier botón para traer contenido desde diferentes APIs
🏆 Desafío final:
¿Puedes crear tu propia versión? Intenta agregar:
- ✅ Una API diferente (clima, noticias, etc.)
- ✅ Botón para guardar favoritos en localStorage
- ✅ Contador de cuántas veces has usado cada API
- ✅ Animaciones cuando cargue el contenido
💡 async/await: La forma moderna
Una vez que domines fetch() básico, puedes usar async/await para código más limpio:
📜 Forma tradicional (.then)
fetch(url)
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch(error => {
console.error(error);
});
✨ Forma moderna (async/await)
async function obtenerDatos() {
try {
const response = await fetch(url);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
¿Cuál usar? Para empezar, domina .then(). Luego aprende async/await. Ambas hacen lo mismo, pero async/await es más fácil de leer.
🎯 ¿Qué sigue después de las APIs?
¡Enhorabuena! Ya sabes conectar tu página web con el mundo real. Tus siguientes pasos:
💡 Consejos de desarrollador profesional
🎯 Para dominar APIs:
- Practica con APIs gratuitas primero
- Lee siempre la documentación
- Usa Postman para probar antes de programar
- Siempre maneja errores
- Muestra estados de carga
🎯 Herramientas útiles:
- Chrome DevTools → Network tab
- Postman para probar APIs
- JSONFormatter para leer respuestas
- Public APIs list en GitHub
- Thunder Client para VS Code
🎯 Proyectos para practicar:
- App del clima de tu ciudad
- Buscador de GIFs
- Generador de memes
- Dashboard de crypto precios
- Buscador de recetas