Saltar al contenido

Lección 1 de 12 · Lectura y práctica

Principios de arquitectura REST

Principios de Arquitectura REST - APIs RESTful con Node.js y Express Principios de Arquitectura REST En esta lección exploraremos los fundamentos teóricos y prácticos que rigen la arquitectura REST (Representational State.

Del concepto a la práctica

Taller de esta lección

Una decisión REST útil es modelar aquello que existe, no la acción que se quiere ejecutar. En una biblioteca, el recurso es un préstamo; “devolver” puede representarse modificando su estado, no creando una ruta verbal como /devolverLibro.

Ejemplo resuelto

Para PATCH /prestamos/42 con { "devuelto": true }, una respuesta válida puede ser 200 y devolver el préstamo actualizado. Si el identificador 42 no existe, corresponde 404; si el cuerpo no contiene un booleano, 400. La representación sigue siendo un préstamo en todos los casos.

Pruébalo

  1. Define GET /libros para colección y GET /libros/7 para elemento.

  2. Define POST /prestamos para crear una relación entre usuario y libro.

  3. Decide qué campos devuelve cada respuesta y cuál es el estado de una creación.

  4. Escribe un caso de inexistencia y otro de entrada inválida.

Error frecuente: usar POST /prestarLibro para todo. Corrígelo separando recurso, método y transición de estado; así el cliente puede comprender el contrato sin conocer una frase interna.

Comprueba tu resultado

Tu diseño es verificable si contiene tres rutas con sustantivos, diferencia colección de elemento, asigna 201 a la creación y reserva 404 para un identificador ausente. El caso inválido no debe modificar ningún préstamo.

Navegador, API y base de datos conectados por solicitudes, consultas y respuestas en sentidos opuestos.
CursaloMapa de práctica
Del navegador a los datos

El navegador envía una solicitud a la API. El servidor valida la operación y consulta los datos; después devuelve una respuesta que el navegador puede mostrar. La base de datos se comunica con el servidor y no directamente con el navegador.

Ver la infografía en tamaño completo ↗

Principios de Arquitectura REST

En esta lección exploraremos los fundamentos teóricos y prácticos que rigen la arquitectura REST (Representational State Transfer). Comprender estos principios es esencial para diseñar APIs robustas, escalables y mantenibles con Node.js y Express.

¿Qué es REST?

REST no es un protocolo ni un formato específico, sino un estilo arquitectónico definido por Roy Fielding en su tesis doctoral en el año 2000. Fielding, uno de los principales autores de HTTP, diseñó REST como una forma de aplicar los principios de HTTP de manera más rigurosa a los servicios web.

CONCEPTO CLAVE
REST es un estilo arquitectónico, no una especificación. Esto significa que existen diferentes grados de "pureza" REST. Una API puede ser más o menos RESTful dependiendo de cuánto adhiera a sus principios fundamentales.

Los 6 Principios Fundamentales de REST

1. Arquitectura Cliente-Servidor

El principio más básico establece una clara separación de responsabilidades. El cliente se encarga de la interfaz de usuario y la presentación, mientras que el servidor gestiona los datos y la lógica de negocio. Esta separación permite que ambos evolucionen de forma independiente.

📌 Aplicación práctica: En Express, esta separación se materializa naturalmente. Tu servidor Express maneja las peticiones y responde con datos, mientras que el cliente (aplicación web, móvil, otro servicio) consume y presenta esos datos según sus necesidades.

2. Sistema de Capas

El cliente no necesita saber si está comunicándose directamente con el servidor final o con un intermediario (proxy, balanceador de carga, caché). Esta opacidad permite añadir capas de infraestructura sin modificar el código del cliente.

// Estructura típica con capas en Express
app.use('/api/v1', cacheMiddleware);     // Capa de caché
app.use('/api/v1', rateLimitMiddleware); // Capa de limitación
app.use('/api/v1', authMiddleware);      // Capa de autenticación
app.use('/api/v1', routes);              // Capa de rutas (lógica de negocio)
app.use('/api/v1', controller);          // Capa de controladores
app.use('/api/v1', model);               // Capa de datos

3. Sin Estado (Stateless)

Quizás el principio más crítico y frecuentemente malinterpretado. Cada petición del cliente al servidor debe contener toda la información necesaria para procesar la solicitud. El servidor no debe almacenar estado de sesión del cliente entre peticiones.

⚠️ Precaución: Confundir "sin estado" con "sin cookies o sin autenticación" es un error común. Los tokens JWT, las API keys y las credenciales en cada petición son perfectamente compatibles con REST. Lo que está prohibido es que el servidor mantenga un estado del cliente entre solicitudes.
💡 Consejo profesional: Aunque REST exige stateless a nivel de protocolo, puedes implementar sesiones server-side si tu arquitectura lo requiere. Sin embargo, en ese caso, tu API ya no sería 100% RESTful. Evalúa si realmente necesitas stateful antes de comprometerte.

4. Interfaz Uniforme

Este principio establece que todos los recursos deben ser accesibles a través de una interfaz genérica. Incluye cuatro sub-restricciones:

  • Identificación de recursos: Cada recurso tiene un identificador único (URI)
  • Manipulación mediante representaciones: El cliente recibe representaciones del recurso (JSON, XML)
  • Mensajes autodescriptivos: Cada mensaje contiene toda la información necesaria
  • Hipermedia como motor de estado (HATEOAS): Las respuestas incluyen enlaces a acciones relacionadas

5. Cacheable

Las respuestas deben indicarse como cacheables o no-cacheables. Una caché bien implementada puede mejorar significativamente el rendimiento. HTTP proporciona headers específicos para esto.

// Ejemplo de configuración de caché en Express
app.get('/api/v1/productos', (req, res) => {
    // Indicar que la respuesta es cacheable por 1 hora
    res.set('Cache-Control', 'public, max-age=3600');
    res.json(productos);
});

app.get('/api/v1/usuarios/:id', (req, res) => {
    // Indicar que NO debe cachearse (datos sensibles)
    res.set('Cache-Control', 'private, no-cache');
    res.json(usuario);
});

6. Código bajo Demanda (Opcional)

Los servidores pueden extender la funcionalidad del cliente transfiriendo código ejecutable (JavaScript en el navegador, por ejemplo). Este principio es opcional y rara vez se implementa completamente en APIs REST modernas.

Recursos y URIs: El Corazón de REST

En REST, todo es un recurso. Un usuario, un producto, una transacción, incluso una búsqueda son recursos. Cada recurso debe tener un identificador único y persistente: el URI (Uniform Resource Identifier).

CONCEPTO CLAVE
Un URI representa un recurso, no una acción. Esto es fundamental. "Obtener usuario con ID 5" no es /obtenerUsuario?id=5, sino /usuarios/5.

Buenas Prácticas para URIs

❌ Incorrecto ✅ Correcto Razón
/getUsuarios /usuarios Los URIs no contienen verbos
/api/get_data /usuarios Usa sustantivos plurales, no "data"
/usuarios/5/productos /productos?usuario=5 Evita anidamiento profundo
/Usuarios /usuarios Usa minúsculas consistentes
📌 Estructura recomendada: https://api.tudominio.com/v1/recurso/{id}/subrecurso. La versión en la URL permite evolucionar tu API sin romper clientes existentes.

Métodos HTTP y sus Semánticas

Una de las elegancia de REST es usar los métodos HTTP existentes con su semántica original. No se trata solo de convenciones: cada método tiene una significado específico que debe respetarse.

Método Semántica Idempotente Seguro Uso común en Express
GET Obtener recurso ✅ Sí ✅ Sí Lectura sin modificación
POST Crear recurso ❌ No ❌ No Crear nuevos elementos
PUT Reemplazar recurso ✅ Sí ❌ No Actualización completa
PATCH Modificar parcialmente ❌ No ❌ No Actualización parcial
DELETE Eliminar recurso ✅ Sí ❌ No Eliminación de recursos
💡 Diferencia clave: Idempotente significa que múltiples llamadas idénticas producen el mismo resultado. DELETE es idempotente porque eliminar un recurso dos veces tiene el mismo efecto (el recurso ya no existe). POST no es idempotente porque crear un usuario dos veces crea dos usuarios diferentes.

Códigos de Estado HTTP

Los códigos de estado son la forma en que el servidor comunica el resultado de una petición. Usarlos correctamente es crucial para una API RESTful profesional.

  1. 2xx - Éxito: La petición fue recibida y procesada correctamente.
  2. 3xx - Redirección: Se requieren acciones adicionales para completar la petición.
  3. 4xx - Error del cliente: La petición tiene problemas de formato o es inválida.
  4. 5xx - Error del servidor: El servidor falló al procesar una petición válida.
// Ejemplo de uso correcto de códigos de estado en Express

// GET /usuarios/5 - Usuario encontrado
res.status(200).json(usuario);

// POST /usuarios - Usuario creado exitosamente
res.status(201).json(nuevoUsuario);

// GET /usuarios/999 - Usuario no encontrado
res.status(404).json({ error: 'Usuario no encontrado' });

// POST /usuarios - Datos inválidos
res.status(400).json({ error: 'El campo "email" es requerido' });

// GET /usuarios - Error interno del servidor
res.status(500).json({ error: 'Error interno del servidor' });
Ver más códigos de estado comunes en APIs REST

Códigos de éxito:

  • 200 - OK (respuesta estándar)
  • 201 - Created (recurso creado)
  • 204 - No Content (sin cuerpo de respuesta)

Códigos de error de cliente:

  • 400 - Bad Request (sintaxis inválida)
  • 401 - Unauthorized (autenticación requerida)
  • 403 - Forbidden (sin permisos)
  • 404 - Not Found (recurso inexistente)
  • 409 - Conflict (conflicto de estado)
  • 422 - Unprocessable Entity (validación fallida)

Códigos de error de servidor:

  • 500 - Internal Server Error
  • 503 - Service Unavailable

HATEOAS: Navegación mediante Hipermedia

HATEOAS (Hypermedia as the Engine of Application State) es quizás el principio de REST más avanzado y menos implementado. Significa que las respuestas de tu API deben incluir enlaces que indiquen las acciones posibles desde ese estado.

⚠️ Importante: HATEOAS añade complejidad significativa. Muchas APIs RESTful populares (GitHub, Stripe) no lo implementan completamente. Es un ideal al que aspirar, no un requisito mínimo.
// Ejemplo de respuesta con HATEOAS
{
  "id": 5,
  "nombre": "Juan Pérez",
  "email": "juan@ejemplo.com",
  "links": [
    { "rel": "self", "href": "/usuarios/5", "method": "GET" },
    { "rel": "actualizar", "href": "/usuarios/5", "method": "PUT" },
    { "rel": "eliminar", "href": "/usuarios/5", "method": "DELETE" },
    { "rel": "pedidos", "href": "/usuarios/5/pedidos", "method": "GET" }
  ]
}
"La interfaz uniforme de REST se define mediante la disponibilidad de recursos de hipermedia en las representaciones devueltas." — Roy Fielding

Ejemplo Práctico: API RESTful con Express

Veamos cómo implementar una API que siga los principios REST utilizando Express:

const express = require('express');
const app = express();
app.use(express.json());

// Base de datos simulada
let usuarios = [
    { id: 1, nombre: 'Ana García', email: 'ana@ejemplo.com' },
    { id: 2, nombre: 'Carlos López', email: 'carlos@ejemplo.com' }
];

// GET /usuarios - Listar todos (colección)
app.get('/api/v1/usuarios', (req, res) => {
    res.status(200).json({
        datos: usuarios,
        total: usuarios.length
    });
});

// GET /usuarios/:id - Obtener uno (recurso individual)
app.get('/api/v1/usuarios/:id', (req, res) => {
    const usuario = usuarios.find(u => u.id === parseInt(req.params.id));
    if (!usuario) {
        return res.status(404).json({ error: 'Usuario no encontrado' });
    }
    res.status(200).json(usuario);
});

// POST /usuarios - Crear nuevo
app.post('/api/v1/usuarios', (req, res) => {
    const { nombre, email } = req.body;
    if (!nombre || !email) {
        return res.status(400).json({ error: 'Nombre y email son requeridos' });
    }
    const nuevoUsuario = {
        id: usuarios.length + 1,
        nombre,
        email
    };
    usuarios.push(nuevoUsuario);
    res.status(201).json(nuevoUsuario);
});

// PUT /usuarios/:id - Reemplazar completamente
app.put('/api/v1/usuarios/:id', (req, res) => {
    const index = usuarios.findIndex(u => u.id === parseInt(req.params.id));
    if (index === -1) {
        return res.status(404).json({ error: 'Usuario no encontrado' });
    }
    const { nombre, email } = req.body;
    if (!nombre || !email) {
        return res.status(400).json({ error: 'Nombre y email son requeridos' });
    }
    usuarios[index] = { id: parseInt(req.params.id), nombre, email };
    res.status(200).json(usuarios[index]);
});

// DELETE /usuarios/:id - Eliminar
app.delete('/api/v1/usuarios/:id', (req, res) => {
    const index = usuarios.findIndex(u => u.id === parseInt(req.params.id));
    if (index === -1) {
        return res.status(404).json({ error: 'Usuario no encontrado' });
    }
    usuarios.splice(index, 1);
    res.status(204).send(); // 204 = éxito sin contenido
});

app.listen(3000, () => console.log('API RESTful ejecutándose en puerto 3000'));
🧠 Quiz

¿Cuál de las siguientes afirmaciones sobre los principios REST es INCORRECTA?

  • A) La arquitectura cliente-servidor establece separación de responsabilidades
  • B) Las APIs REST deben ser obligatoriamente stateful para mantener sesiones
  • C) Los códigos de estado HTTP comunican el resultado de las peticiones
  • D) DELETE es idempotente mientras que POST no lo es
✅ Respuesta: B) Las APIs REST deben ser obligatoriamente stateful para mantener sesiones. Esta afirmación es incorrecta porque REST es fundamentalmente stateless; cada petición debe contener toda la información necesaria sin rely en estado almacenado en el servidor entre peticiones.

Conclusión

Los principios de arquitectura REST proporcionan una base sólida para diseñar APIs HTTP que son escalables, simples y mantenibles. Aunque en la práctica muchas APIs "RESTful" no siguen todos los principios al 100%, comprenderlos te permitirá tomar decisiones informadas sobre cuándo adherirte estrictamente y cuándo hacer compromisos pragmáticos.

📌 Próximos pasos: En la siguiente lección, profundizaremos en la implementación práctica de estos principios con Express, incluyendo manejo de rutas, middleware y estructuras de proyecto recomendadas.

Práctica acompañada: Una interfaz web de principio a fin

Estado, formularios y una API pequeña. Usa este cuaderno como práctica complementaria de la ruta; el material original de la lección continúa siendo tu referencia.

Estado, formularios y una API pequeña: HTML, Estado, Validación, Respuesta
De la idea al resultado: una secuencia para practicar y revisar.

Práctica: cuatro pruebas de comportamiento

Prueba un título normal, solo espacios, 121 caracteres y una falla de red simulada. Registra la respuesta y el estado de la interfaz. Repite usando solo teclado: debes llegar al campo, enviarlo y encontrar el error sin usar el ratón.

Después agrega una segunda tarea y cambia el orden de la lista. Comprueba que cada botón sigue actuando sobre el elemento correcto. Si trabajas con microservicios, dibuja qué servicio posee el dato y qué ocurre si la dependencia no responde. No necesitas una infraestructura distribuida para aprender a definir tiempos de espera y errores comprensibles.

Comprueba tu entrega

Entrega el código, el contrato de API y evidencia de las cuatro pruebas. Indica si los datos se guardan o desaparecen al recargar. Una demostración en memoria debe decirlo para que nadie confunda un prototipo con almacenamiento persistente.

Sigue con Construcción de APIs RESTful con Node.js y Express

Esta lección forma parte de un curso de Cursalo. Revisa su programa y elige entre la compra individual o el acceso a toda la escuela.

Si ya tenías acceso a este curso, entra con tu cuenta: tus derechos y tu progreso se conservan. No necesitas comprarlo de nuevo.

01 / ELEGIR UNA RUTA

El próximo paso

Aprende IA mientras construyes algo que sí necesitas.

Empieza por el curso inicial o explora el catálogo para encontrar una práctica alineada con tu trabajo.