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

  1. Cliente-Servidor: separación clara entre quien consume (cliente) y quien provee (servidor).
  2. Sin estado (Stateless): cada solicitud contiene toda la información necesaria; el servidor no recuerda solicitudes anteriores.
  3. Cacheable: las respuestas pueden marcarse como cacheables para mejorar rendimiento.
  4. Interfaz uniforme: URLs consistentes, uso correcto de verbos HTTP, formato estándar.
  5. Sistema por capas: el cliente no sabe si habla directamente al servidor o a un proxy/balanceador.
  6. 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étodoAcciónIdempotenteEjemplo de uso
GETLeer/consultarObtener lista de usuarios
POSTCrear nuevo recursoNoCrear un usuario nuevo
PUTReemplazar recurso completoActualizar todos los datos del usuario
PATCHActualizar parcialmenteNo siempreCambiar solo el email del usuario
DELETEEliminar recursoEliminar 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ódigoSignificadoCuándo usarlo
200 OKÉxitoGET, PUT, PATCH exitosos
201 CreatedRecurso creadoPOST exitoso
204 No ContentÉxito sin cuerpoDELETE exitoso
400 Bad RequestSolicitud inválidaDatos malformados o faltantes
401 UnauthorizedNo autenticadoToken faltante o inválido
403 ForbiddenSin permisoAutenticado pero sin acceso
404 Not FoundNo encontradoRecurso no existe
409 ConflictConflictoEmail duplicado al registrar
422 UnprocessableValidación fallidaEmail con formato inválido
500 Internal ErrorError del servidorBug 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

APIURLDatos
JSONPlaceholderjsonplaceholder.typicode.comUsuarios, posts, comentarios (mock)
OpenWeatherMapopenweathermap.org/apiClima en tiempo real
PokéAPIpokeapi.coDatos de Pokémon
REST Countriesrestcountries.comInformación de países
The Movie DBthemoviedb.org/documentation/apiPelículas y series

Resumen del Capítulo