Programación Web · Capítulo 14
APIs REST: Diseño y Consumo de Interfaces de Programación
Entiende cómo las aplicaciones se comunican entre sí y aprende a diseñar y consumir APIs profesionales.
1. ¿Qué es una API?
Una API (Application Programming Interface) es un contrato que define cómo dos sistemas de software se comunican. Cuando una aplicación necesita datos o servicios de otra, los solicita a través de su API.
Piensa en una API como el menú de un restaurante: el menú te dice qué platos puedes pedir (endpoints disponibles), cómo pedirlos (formato de la solicitud) y qué recibirás a cambio (formato de la respuesta). No necesitas saber cómo funciona la cocina por dentro.
REST (Representational State Transfer) es un estilo arquitectónico para diseñar APIs web. No es un protocolo ni un estándar — es un conjunto de principios que hacen las APIs predecibles, escalables y fáciles de usar.
2. Los 6 Principios de REST
- Cliente-Servidor: separación clara entre quien consume (cliente) y quien provee (servidor).
- Sin estado (Stateless): cada solicitud contiene toda la información necesaria; el servidor no recuerda solicitudes anteriores.
- Cacheable: las respuestas pueden marcarse como cacheables para mejorar rendimiento.
- Interfaz uniforme: URLs consistentes, uso correcto de verbos HTTP, formato estándar.
- Sistema por capas: el cliente no sabe si habla directamente al servidor o a un proxy/balanceador.
- Código bajo demanda (opcional): el servidor puede enviar código ejecutable al cliente.
3. Métodos HTTP y Cuándo Usar Cada Uno
| Método | Acción | Idempotente | Ejemplo de uso |
| GET | Leer/consultar | Sí | Obtener lista de usuarios |
| POST | Crear nuevo recurso | No | Crear un usuario nuevo |
| PUT | Reemplazar recurso completo | Sí | Actualizar todos los datos del usuario |
| PATCH | Actualizar parcialmente | No siempre | Cambiar solo el email del usuario |
| DELETE | Eliminar recurso | Sí | Eliminar un usuario |
Idempotente significa que hacer la misma operación múltiples veces produce el mismo resultado que hacerla una sola vez. GET /usuarios siempre retorna la misma lista; DELETE /usuarios/5 siempre deja al usuario 5 eliminado.
4. Códigos de Estado HTTP
| Código | Significado | Cuándo usarlo |
| 200 OK | Éxito | GET, PUT, PATCH exitosos |
| 201 Created | Recurso creado | POST exitoso |
| 204 No Content | Éxito sin cuerpo | DELETE exitoso |
| 400 Bad Request | Solicitud inválida | Datos malformados o faltantes |
| 401 Unauthorized | No autenticado | Token faltante o inválido |
| 403 Forbidden | Sin permiso | Autenticado pero sin acceso |
| 404 Not Found | No encontrado | Recurso no existe |
| 409 Conflict | Conflicto | Email duplicado al registrar |
| 422 Unprocessable | Validación fallida | Email con formato inválido |
| 500 Internal Error | Error del servidor | Bug no manejado en el servidor |
5. Diseño de URLs: Buenas Prácticas
Regla de oro: Las URLs representan sustantivos (recursos), no verbos (acciones). El verbo HTTP ya indica la acción.
❌ MALO — verbos en las URLs
GET /obtenerUsuarios
POST /crearUsuario
PUT /actualizarUsuario/5
GET /borrarUsuario?id=5
✅ BUENO — sustantivos, recursos anidados lógicos
GET /api/v1/usuarios → listar todos
POST /api/v1/usuarios → crear uno nuevo
GET /api/v1/usuarios/5 → obtener usuario 5
PUT /api/v1/usuarios/5 → reemplazar usuario 5
PATCH /api/v1/usuarios/5 → actualizar parcialmente
DELETE /api/v1/usuarios/5 → eliminar usuario 5
GET /api/v1/usuarios/5/pedidos → pedidos del usuario 5
GET /api/v1/usuarios/5/pedidos/12 → pedido 12 del usuario 5
// Filtros como query params
GET /api/v1/productos?categoria=ropa&minPrecio=100&pagina=2
6. Formato JSON
Las APIs REST usan JSON (JavaScript Object Notation) para estructurar los datos. Es legible por humanos y fácilmente procesable por máquinas.
// Respuesta exitosa bien estructurada
{
"exito": true,
"datos": {
"id": 42,
"nombre": "Ana García",
"email": "ana@ejemplo.com",
"rol": "estudiante",
"creadoEn": "2024-01-15T10:30:00Z"
},
"meta": {
"version": "1.0"
}
}
// Respuesta de lista con paginación
{
"exito": true,
"datos": [
{ "id": 1, "nombre": "Producto A" },
{ "id": 2, "nombre": "Producto B" }
],
"paginacion": {
"pagina": 1,
"porPagina": 20,
"total": 150,
"totalPaginas": 8
}
}
// Respuesta de error bien estructurada
{
"exito": false,
"error": {
"codigo": "VALIDACION_FALLIDA",
"mensaje": "Los datos enviados contienen errores",
"detalles": [
{ "campo": "email", "error": "Formato de email inválido" },
{ "campo": "nombre", "error": "El nombre es obligatorio" }
]
}
}
7. Autenticación en APIs
API Keys
// Enviar API Key en el header (más seguro que en la URL)
GET /api/v1/datos
Headers:
X-API-Key: tu_clave_api_aqui
// En JavaScript con fetch:
const respuesta = await fetch("https://api.ejemplo.com/v1/datos", {
headers: {
"X-API-Key": process.env.API_KEY
}
});
Bearer Tokens (JWT)
// Flujo de autenticación con JWT:
// 1. El usuario inicia sesión
POST /api/auth/login
Body: { "email": "ana@ejemplo.com", "password": "mi_contraseña" }
// 2. El servidor responde con un token
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
// 3. El cliente incluye el token en cada solicitud posterior
GET /api/v1/mi-perfil
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
// En JavaScript:
const token = localStorage.getItem("token");
const respuesta = await fetch("/api/v1/mi-perfil", {
headers: {
"Authorization": `Bearer ${token}`
}
});
8. Consumir una API con fetch y axios
// Con fetch (nativo del navegador)
async function obtenerClima(ciudad) {
const API_KEY = "tu_clave_aqui";
const url = `https://api.openweathermap.org/data/2.5/weather?q=${ciudad}&appid=${API_KEY}&units=metric&lang=es`;
try {
const res = await fetch(url);
if (!res.ok) {
const error = await res.json();
throw new Error(error.message || `Error ${res.status}`);
}
const datos = await res.json();
return {
ciudad: datos.name,
temperatura: datos.main.temp,
descripcion: datos.weather[0].description,
humedad: datos.main.humidity
};
} catch (error) {
console.error("Error al obtener clima:", error);
throw error;
}
}
// Con axios (biblioteca popular, más features)
// npm install axios
import axios from "axios";
const api = axios.create({
baseURL: "https://api.ejemplo.com/v1",
headers: { "Authorization": `Bearer ${token}` },
timeout: 5000
});
// GET
const { data } = await api.get("/usuarios");
// POST
const { data: nuevo } = await api.post("/usuarios", {
nombre: "Ana",
email: "ana@ejemplo.com"
});
// Interceptor para manejar errores globalmente
api.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
// Token expirado — redirigir al login
window.location.href = "/login";
}
return Promise.reject(error);
}
);
9. APIs Públicas para Practicar
| API | URL | Datos |
| JSONPlaceholder | jsonplaceholder.typicode.com | Usuarios, posts, comentarios (mock) |
| OpenWeatherMap | openweathermap.org/api | Clima en tiempo real |
| PokéAPI | pokeapi.co | Datos de Pokémon |
| REST Countries | restcountries.com | Información de países |
| The Movie DB | themoviedb.org/documentation/api | Películas y series |
Resumen del Capítulo
- Una API REST es un contrato de comunicación entre sistemas; REST es un conjunto de principios arquitectónicos, no un estándar rígido.
- Los métodos HTTP expresan la intención: GET (leer), POST (crear), PUT (reemplazar), PATCH (actualizar parcial), DELETE (eliminar).
- Los códigos de estado comunican el resultado: 2xx éxito, 4xx error del cliente, 5xx error del servidor.
- Las URLs deben ser sustantivos jerárquicos (/usuarios/5/pedidos), nunca verbos; usa query params para filtros y paginación.
- La autenticación se hace con API Keys (cabecera X-API-Key) o tokens JWT (cabecera Authorization: Bearer).
- fetch es nativo y suficiente para la mayoría de casos; axios añade interceptores, cancelación y mejor manejo de errores.
- Diseña las respuestas con estructura consistente: siempre incluye un campo de éxito/error y mensajes descriptivos para facilitar el debugging.