Fragua Tech

Desarrollo Web Backend

Parte 1

Clase 6 — Cliente-servidor, APIs REST y Express

Objetivos de aprendizaje

Al terminar esta clase, podrás levantar tu propio servidor y diseñar APIs limpias que cualquier frontend pueda consumir.

🏠

Cliente-Servidor

Entender cómo se comunican frontend y backend a través de HTTP.

🔗

APIs REST

Diseñar endpoints siguiendo convenciones REST y elegir verbos y códigos.

🏗️

Arquitecturas

Diferenciar monolito, microservicios y serverless, y saber cuándo usar cada uno.

Node.js + Express

Crear un servidor con rutas, middleware y un CRUD completo.

El modelo cliente-servidor

Hasta ahora trabajamos el frontend: lo que el usuario ve. Detrás hay un backend que hace el trabajo pesado.

📱

Frontend

El salón: la carta, el mesero, la presentación del plato. React, HTML, CSS — todo en el navegador del usuario.

🔥

Backend

La cocina: recibe pedidos, procesa ingredientes, prepara la comida. Vive en un servidor remoto, lejos del usuario.

📊

Base de datos

La despensa: almacena los ingredientes. El backend la consulta, el frontend nunca la toca directamente.

Por qué separarlos

Seguridad (la lógica crítica nunca llega al cliente), reutilización (una API sirve a web, móvil y otros sistemas) y escalabilidad (puedes desplegar y escalar cada parte por separado).

Qué hace exactamente un backend

Cinco responsabilidades que rara vez deberían vivir en el frontend.

💰 Lógica de negocio

Calcular precios con impuestos, aplicar descuentos, validar reglas que cambian sin redeploy del frontend.

💾 Persistencia

Hablar con la base de datos para guardar, leer, actualizar y borrar datos. Único punto de acceso a esos datos.

🔐 Autenticación y autorización

¿Quién es el usuario? (autenticación) ¿Qué le dejas hacer? (autorización). Tokens, sesiones, permisos.

📧 Tareas en segundo plano

Enviar emails, procesar pagos, generar PDFs, sincronizar datos. Todo lo que el usuario no puede esperar mirando una pantalla.

🔌 Integración con otros servicios

Llamar a APIs externas (Stripe, OpenAI, AWS), agregar datos de varias fuentes y devolver una respuesta unificada al frontend.

HTTP es stateless: cada petición es una isla

El servidor no recuerda nada entre peticiones. Cada request lleva consigo todo lo que necesita: URL, headers, body, credenciales. Esto es lo que permite que un backend sirva a millones de clientes en paralelo.

Ventajas

  • • Escalado horizontal: añadir servidores no es un drama
  • • Caché: responses idénticas se pueden guardar
  • • Resiliencia: si un servidor cae, otro toma la petición

Implicaciones

  • • Login: en cada request hay que enviar un token o cookie
  • • Estado del usuario vive en la base de datos, no en memoria
  • • Las sesiones se modelan, no son automáticas
Material complementario

HTTP — anatomía de una petición y una respuesta

Toda la web habla este idioma. Una petición tiene cuatro partes y una respuesta, también.

Request
GET /api/productos?limit=10 HTTP/1.1
Host: api.tienda.com
Accept: application/json
Authorization: Bearer eyJhbGc...
User-Agent: Mozilla/5.0 ...
// (sin body en GET)
  • Línea de petición: verbo, ruta, versión
  • Headers: metadatos (auth, formato, host)
  • Body: datos (en POST/PUT/PATCH)
Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 142
Cache-Control: max-age=60
{ "productos": [
  { "id": 1, "nombre": "Laptop" }
] }
  • Status line: versión, código, frase
  • Headers: tipo de contenido, caché, CORS
  • Body: el dato pedido (JSON, HTML, binario)

Headers que verás todos los días

HeaderPara qué sirve
Content-TypeFormato del body: application/json, text/html, multipart/form-data...
AcceptFormato que el cliente espera de respuesta.
AuthorizationCredenciales: Bearer <token>, Basic ....
Cookie / Set-CookieSesiones, preferencias y tracking persistentes en el navegador.
Cache-ControlCuánto tiempo vale guardar la respuesta en caché.
User-AgentQuién hace la petición: navegador, librería, bot.
Access-Control-Allow-OriginCORS: qué orígenes pueden consumir esta API desde el navegador.
Material complementario

API: el contrato entre dos sistemas

API(Application Programming Interface) es la forma en la que un programa expone sus capacidades a otro. Define qué se puede pedir, cómo y qué te van a devolver.

Web API / REST

Recursos accesibles vía HTTP, JSON como formato. Lo más común hoy.

GraphQL

Un solo endpoint, el cliente describe la forma exacta de los datos que necesita.

gRPC / WebSocket

Comunicación binaria de baja latencia y conexiones bidireccionales en tiempo real.

En esta clase

Nos centramos en REST sobre HTTP con JSON: es lo que más vas a encontrar en el mundo real y lo que casi todo framework soporta de fábrica.

JSON — el formato común de la web

JavaScript Object Notation. Texto plano, legible para humanos y trivial de parsear en cualquier lenguaje. Lo usan REST, configuraciones, logs y casi todo.

{
  "id": 42,
  "nombre": "Laptop",
  "disponible": true,
  "etiquetas": ["nuevo", "oferta"],
  "stock": null,
  "specs": {
    "ram": 16,
    "ssd": 512
  }
}

Tipos disponibles

string, number, boolean, null, array y object. No hay funciones, fechas ni undefined: las fechas viajan como string ISO.

Reglas estrictas

Claves entre comillas dobles. Sin trailing commas. Sin comentarios. Una coma de más rompe el parser.

En Node

JSON.stringify(obj) serializa,JSON.parse(texto) deserializa. Express lo hace por ti conexpress.json().

Material complementario

REST — Representational State Transfer

REST no es un estándar formal: es unestilo arquitectónico que Roy Fielding describió en 2000 y que la industria adoptó como convención común.

Principios clave

  • Recursos: cada cosa tiene una URL única
  • Verbos HTTP: la acción la indica el método
  • Stateless: cada petición es independiente
  • Representaciones: JSON, XML, HTML... el cliente elige
  • Capas: cachés y proxies entre cliente y servidor

Por qué se impuso

  • • Aprovecha HTTP tal cual, sin protocolos extra
  • • Cualquier cliente (browser, móvil, curl) lo puede usar
  • • Cacheable de fábrica con headers HTTP
  • • Convenciones claras → menos discusiones de equipo
  • • Tooling masivo: Postman, Swagger, librerías para todo

Recursos: las "cosas" de tu API

En REST piensas en sustantivos: productos, usuarios, pedidos. Cada recurso tiene una URL y se opera con verbos HTTP.

// Colección: lista de productos
GET    /api/productos
POST   /api/productos
// Recurso individual: producto 42
GET    /api/productos/42
PUT    /api/productos/42
PATCH  /api/productos/42
DELETE /api/productos/42
// Sub-recurso: reviews del producto 42
GET    /api/productos/42/reviews
POST   /api/productos/42/reviews

Path params

Identifican un recurso concreto:/productos/42.

Query params

Filtros, paginación, búsqueda:?categoria=ropa&page=2&limit=20.

Body

Datos para crear o actualizar (POST, PUT, PATCH). Suele ser JSON.

Diseño RESTful — bien vs. mal

Las reglas de oro que separan una API agradable de una pesadilla mantenible.

Bien diseñado
GET    /api/v1/productos
GET    /api/v1/productos/42
POST   /api/v1/productos
PATCH  /api/v1/productos/42
DELETE /api/v1/productos/42
GET    /api/v1/usuarios/7/pedidos
GET    /api/v1/tipos-producto
Mal diseñado
GET  /api/obtenerProductos
GET  /api/producto
POST /api/crearNuevoProducto
GET  /api/producto/eliminar/42
POST /api/PRODUCTOS_v2/UPDATE
GET  /api/usuario_pedidos?uid=7
GET  /api/tipoProducto

Reglas de oro

  • Sustantivos en plural: /usuarios, no /obtenerUsuario
  • Jerarquía con /: /usuarios/7/pedidos
  • kebab-case en rutas multi-palabra: /tipos-producto
  • Versiona la API: /api/v1/... permite evolucionar sin romper clientes
  • GET nunca modifica datos. Si un buscador rastrea, ¿borraría tus recursos?
Material complementario

Verbos HTTP — qué hace cada uno

Pulsa cualquier verbo para ver qué pasa por la red en cada caso.

GET200 OK

Uso: Leer un recurso o lista de recursos. No modifica datos.

Idempotente: Sí — repetirlo da el mismo resultado.

// Petición
GET /api/productos/42 HTTP/1.1
Host: api.tienda.com
Accept: application/json
// Respuesta
HTTP/1.1 200 OK
Content-Type: application/json
 
{ "id": 42, "nombre": "Laptop", "precio": 999 }
POST201 Created

Uso: Crear un recurso nuevo en una colección.

Idempotente: No — cada llamada crea un recurso distinto.

// Petición
POST /api/productos HTTP/1.1
Host: api.tienda.com
Content-Type: application/json
 
{ "nombre": "Teclado", "precio": 79 }
// Respuesta
HTTP/1.1 201 Created
Location: /api/productos/43
 
{ "id": 43, "nombre": "Teclado", "precio": 79 }
PUT200 OK

Uso: Reemplazar por completo un recurso existente.

Idempotente: Sí — repetirlo deja el recurso en el mismo estado.

// Petición
PUT /api/productos/42 HTTP/1.1
Host: api.tienda.com
Content-Type: application/json
 
{ "nombre": "Laptop Pro", "precio": 1299 }
// Respuesta
HTTP/1.1 200 OK
Content-Type: application/json
 
{ "id": 42, "nombre": "Laptop Pro", "precio": 1299 }
PATCH200 OK

Uso: Actualizar parcialmente: solo los campos enviados.

Idempotente: Generalmente sí (depende de la implementación).

// Petición
PATCH /api/productos/42 HTTP/1.1
Host: api.tienda.com
Content-Type: application/json
 
{ "precio": 899 }
// Respuesta
HTTP/1.1 200 OK
Content-Type: application/json
 
{ "id": 42, "nombre": "Laptop", "precio": 899 }
DELETE204 No Content

Uso: Eliminar un recurso.

Idempotente: Sí — borrar lo que ya no existe sigue dejándolo borrado.

// Petición
DELETE /api/productos/42 HTTP/1.1
Host: api.tienda.com
// Respuesta
HTTP/1.1 204 No Content
(sin cuerpo)

Códigos de estado — el "rendir cuentas" del servidor

Tres dígitos que resumen el resultado. El primero es el más importante.

1xx · Info

Raras de ver. 100 Continue, 101 Switching Protocols(WebSockets).

2xx · Éxito

  • 200 OK — todo bien
  • 201 Created — recurso creado
  • 204 No Content — bien, sin body

4xx · Cliente

  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 409 Conflict
  • 422 Unprocessable
  • 429 Too Many Requests

5xx · Servidor

  • 500 Internal Error
  • 502 Bad Gateway
  • 503 Service Unavailable
  • 504 Gateway Timeout

Diferenciar 401 vs 403

401 Unauthorized: "no sé quién eres" — falta o es inválido el token.403 Forbidden: "sé quién eres pero no te dejo" — autenticado, sin permiso.

Material complementario

Pon a prueba tu intuición

Lee cada escenario y elige el código de estado correcto.

Asigna a cada escenario el código HTTP que devolverías como API.

Creaste un usuario nuevo y todo salió bien.

Pediste /api/usuarios/9999 pero no existe.

Enviaste un POST sin token de autenticación.

El servidor explotó por un bug en el código.

Borraste un recurso y no hay nada que devolver.

Cómo organizar el backend: tres caminos

No hay arquitectura "mejor": hay arquitectura adecuada al tamaño del problema.

Monolito

Todo el backend en una sola aplicación que se despliega como un bloque.

Ventajas: simple de desarrollar, desplegar y debuggear. Ideal al empezar.

Cuidado: conforme crece, se convierte en un "big ball of mud" si no hay disciplina.

Microservicios

Cada funcionalidad es un servicio independiente que se comunica por la red.

Ventajas: equipos autónomos, escala por servicio, fallos aislados.

Cuidado: complejidad operacional enorme — observabilidad, despliegue, latencias de red, datos distribuidos.

Serverless

Funciones que ejecuta el cloud bajo demanda: AWS Lambda, Vercel, Cloudflare Workers.

Ventajas: sin servidores que gestionar, escala automático, pagas por ejecución.

Cuidado: cold starts, límites de tiempo, vendor lock-in.

Empieza simple, complica solo cuando duela

Sí: empieza con monolito

  • • Eres una persona o un equipo pequeño
  • • No conoces todavía bien el dominio
  • • Quieres iterar y descartar ideas rápido
  • • Tu app aún no tiene picos de tráfico monstruosos

Considera microservicios cuando…

  • • Múltiples equipos pisándose el código
  • • Una parte tiene escala muy distinta del resto
  • • Distintos lenguajes / runtimes por área
  • • Tienes plataforma para soportarlo (k8s, observability)

Antipatrón clásico

Empezar un proyecto con 12 microservicios "porque es lo moderno". Casi siempre acabas con unmonolito distribuido: la complejidad de microservicios sin los beneficios.

Material complementario

Node.js — JavaScript fuera del navegador

En 2009 Ryan Dahl tomó el motor V8 de Chrome y lo empaquetó como un runtime de servidor. El resultado: el mismo lenguaje en frontend y backend.

Qué te da Node

  • • Un runtime JS rápido (V8) en el servidor
  • • APIs para ficheros, red, procesos, streams
  • npm, el registro de paquetes más grande del mundo
  • • Modelo asíncrono no bloqueante (event loop)

El event loop en una frase

Node no abre un hilo por petición: usa un único hilo que delega la I/O al sistema operativo y atiende lo siguiente que esté listo. Por eso aguanta miles de conexiones simultáneas con poca memoria.

Alternativas modernas

Deno yBun son runtimes JS más recientes con TypeScript de fábrica y mejoras de rendimiento. La industria todavía corre casi todo en Node, pero merece la pena conocerlos.

Configurar un proyecto desde cero

Cuatro comandos y ya tienes un servidor Express listo para escribir endpoints.

mkdir mi-api && cd mi-api
npm init -y
npm install express
npm install -D nodemon
# añade en package.json:
# "scripts": { "dev": "nodemon server.js" }
npm run dev

package.json

Manifiesto del proyecto: nombre, versión, dependencias y scripts. Nunca lo edites a mano para añadir paquetes — usa npm install.

node_modules/

Carpeta con todas las dependencias instaladas. Pesa mucho:.gitignore siempre.

nodemon

Reinicia el servidor automáticamente cuando guardas un archivo. Sólo para desarrollo (de ahí -D).

Material complementario

Express — el framework web minimalista

Express te da un router HTTP, middleware y poco más. Su sencillez es justo lo que lo hizo el framework más usado del ecosistema Node.

// server.js
const express = require('express');
const app = express();
const PORT = 3000;
app.use(express.json());
app.get('/', (req, res) => {
  res.json({ mensaje: 'Hola Backend' });
});
app.listen(PORT, () => {
  console.log(`http://localhost:${PORT}`);
});

app

Instancia de Express. Encadenas .use(), .get(),.post()... y al final .listen().

req, res

La petición y la respuesta. req.params,req.query, req.body, req.headers. En res usas res.json(), res.status(),res.send().

express.json()

Middleware integrado que parsea bodies JSON entrantes y los deja enreq.body. Sin esto, req.body es undefined.

Rutas, parámetros y respuestas

Tres formas de entrar datos al endpoint y cómo responder con el código adecuado.

// Path param: /api/usuarios/42
app.get('/api/usuarios/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const u = usuarios.find(u => u.id === id);
  if (!u) return res.status(404).json({ error: 'No existe' });
  res.json(u);
});
// Query params: /api/buscar?q=laptop&limit=10
app.get('/api/buscar', (req, res) => {
  const { q, limit = 20 } = req.query;
  res.json(buscar(q, +limit));
});
// Body: POST /api/usuarios
app.post('/api/usuarios', (req, res) => {
  const { nombre, email } = req.body;
  if (!nombre) return res.status(400).json({ error: 'nombre requerido' });
  const nuevo = crear(nombre, email);
  res.status(201).json(nuevo);
});

Una API CRUD completa, en 30 líneas

Lista, lee, crea, actualiza y borra tareas. Sin base de datos: por ahora vivimos en un array en memoria.

let tareas = [{ id: 1, titulo: 'Aprender Express', hecha: false }];
let next = 2;
// LIST
app.get('/api/tareas', (_, res) => res.json(tareas));
// READ
app.get('/api/tareas/:id', (req, res) => {
  const t = tareas.find(t => t.id === +req.params.id);
  if (!t) return res.status(404).json({ error: 'No existe' });
  res.json(t);
});
// CREATE
app.post('/api/tareas', (req, res) => {
  const { titulo } = req.body;
  if (!titulo) return res.status(400).json({ error: 'titulo requerido' });
  const t = { id: next++, titulo, hecha: false };
  tareas.push(t);
  res.status(201).json(t);
});
// UPDATE parcial
app.patch('/api/tareas/:id', (req, res) => {
  const t = tareas.find(t => t.id === +req.params.id);
  if (!t) return res.status(404).json({ error: 'No existe' });
  Object.assign(t, req.body);
  res.json(t);
});
// DELETE
app.delete('/api/tareas/:id', (req, res) => {
  tareas = tareas.filter(t => t.id !== +req.params.id);
  res.status(204).send();
});
Material complementario

Middleware — el corazón de Express

Una función con firma (req, res, next) que se ejecuta entre la petición y la respuesta. Cada middleware puede leer, modificar, responder o pasar el control con next().

  1. 1

    Petición entra

    POST /api/tareas con { titulo: "Comprar pan" }

  2. 2

    Middleware: logger

    Imprime fecha, método y URL en consola.

  3. 3

    Middleware: express.json()

    Parsea el body JSON y lo deja en req.body.

  4. 4

    Middleware: auth

    Valida cabecera x-api-key. Si falta → 401.

  5. 5

    Middleware: validador

    Comprueba que titulo no esté vacío. Si lo está → 400.

  6. 6

    Handler de ruta

    Crea la tarea y devuelve 201 con el JSON.

  7. 7

    Respuesta sale

    HTTP/1.1 201 Created — JSON al cliente.

Cada middleware llama a next() para pasar el control al siguiente. Si alguno responde, la cadena se detiene.

Patrones que verás en cualquier API

Logger, validador, autenticación, rate limiter, manejador global de errores.

// 1) Logger global
app.use((req, res, next) => {
  console.log(`${new Date().toISOString()} ${req.method} ${req.url}`);
  next();
});
// 2) Auth: solo deja pasar si hay API key
const apiKey = (req, res, next) => {
  if (req.headers['x-api-key'] !== process.env.API_KEY) {
    return res.status(401).json({ error: 'Token inválido' });
  }
  next();
};
// 3) Validación específica
const validarTarea = (req, res, next) => {
  if (!req.body.titulo?.trim()) {
    return res.status(400).json({ error: 'titulo requerido' });
  }
  next();
};
// 4) Encadenar varios en una ruta
app.post('/api/tareas', apiKey, validarTarea, handler);
// 5) Manejador global de errores (4 args)
app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).json({ error: 'Error interno' });
});
Material complementario

CORS — el muro entre dominios

Por seguridad, el navegador bloquea peticiones desde un origen (localhost:5173) hacia otro (api.tienda.com) salvo que el servidor lo permita explícitamente. Eso es CORS.

// Activar CORS en Express
const cors = require('cors');
// Permite cualquier origen (sólo dev)
app.use(cors());
// Configuración granular en producción
app.use(cors({
  origin: ['https://midominio.com'],
  methods: ['GET', 'POST', 'PATCH', 'DELETE'],
  credentials: true,
}));

Síntomas típicos

En la consola del navegador:"Access to fetch at ... has been blocked by CORS policy". La petición sí sale, pero el navegador bloquea la respuesta.

Preflight

Para verbos no triviales, el navegador envía primero unOPTIONS. Si la respuesta no autoriza el origen y el método, el real ni siquiera se manda.

No es seguridad

CORS protege al usuario en el navegador. curl, Postman o tu propio backend pueden seguir llamando: la API necesita autenticación de verdad.

Probar tu API sin frontend

Antes de conectar el frontend conviene comprobar manualmente que la API responde como esperas. Tres herramientas indispensables.

curl

Línea de comandos. Disponible en cualquier sistema. Ideal para scripts y documentación reproducible.

curl -X POST \
  -H 'Content-Type: application/json' \
  -d '{"titulo":"Comprar pan"}' \
http://localhost:3000/api/tareas

Postman / Insomnia

GUI completa: colecciones de peticiones, entornos por servidor, variables, scripts y compartir con el equipo. El estándar de facto.

Bonus: pueden generar código (curl, fetch, axios...) con un clic.

Thunder Client / REST Client

Extensiones de VS Code. Sin salir del editor escribes la petición en un.http y la lanzas con F1.

POST http://localhost:3000/api/tareas
Content-Type: application/json
{ "titulo": "Test" }
Material complementario

Resumen de la Clase 6

ConceptoDefinición
BackendParte del servidor que procesa lógica, datos y seguridad
HTTPProtocolo stateless de petición/respuesta sobre el que corre la web
APIContrato que define cómo dos sistemas se comunican
RESTEstilo arquitectónico basado en recursos, URLs y verbos HTTP
EndpointCombinación de verbo + URL que expone una operación
Verbos HTTPGET, POST, PUT, PATCH, DELETE — la acción sobre el recurso
Códigos de estado2xx éxito, 4xx error de cliente, 5xx error de servidor
JSONFormato textual ligero para intercambiar datos estructurados
MonolitoBackend como una sola aplicación; punto de partida razonable
MicroserviciosServicios independientes que se comunican por la red
ServerlessFunciones que ejecuta el cloud bajo demanda (Lambda, Vercel...)
Node.jsRuntime de JavaScript en el servidor con event loop no bloqueante
npmGestor de paquetes del ecosistema Node y registro público
ExpressFramework web minimalista para Node.js
MiddlewareFunción (req, res, next) que se ejecuta en la cadena de la petición
CORSPolítica del navegador que regula peticiones cross-origin
Material complementario
Básico

Ejercicio 6.1: Diseñar una API REST

Sin escribir código, diseña los endpoints de una biblioteca digital. Debe gestionar libros, usuarios, préstamos y devoluciones.

VerboRutaDescripciónÉxito
GET/api/librosListar todos los libros200
GET/api/libros/:idObtener un libro200
POST/api/librosCrear un libro201
PUT/api/libros/:idReemplazar un libro200
DELETE/api/libros/:idEliminar un libro204
POST/api/usuarios/:id/prestamosPedir prestado un libro201
DELETE/api/usuarios/:id/prestamos/:libroIdDevolver un libro204
GET/api/usuarios/:id/prestamosPréstamos activos del usuario200

Fíjate en el patrón /usuarios/:id/prestamos: el préstamo es unsub-recurso que vive bajo un usuario.

Básico

Ejercicio 6.2: Tu primer servidor Express

Cuatro rutas que cubren path params, query params y body.

Rutas pedidas

  • GET /{ mensaje: 'Servidor funcionando' }
  • GET /api/saludo/:nombre{ saludo: 'Hola, [nombre]!' }
  • GET /api/suma?a=5&b=3{ resultado: 8 }
  • POST /api/eco → devuelve el body recibido
const express = require('express');
const app = express();
app.use(express.json());
app.get('/', (_, res) =>
  res.json({ mensaje: 'Servidor funcionando' }));
app.get('/api/saludo/:nombre', (req, res) =>
  res.json({ saludo: `Hola, ${req.params.nombre}!` }));
app.get('/api/suma', (req, res) => {
  const a = parseFloat(req.query.a) || 0;
  const b = parseFloat(req.query.b) || 0;
  res.json({ resultado: a + b });
});
app.post('/api/eco', (req, res) => res.json(req.body));
app.listen(3000);
Intermedio

Ejercicio 6.3: API CRUD de notas

Implementa la API y prueba en este simulador. Validación de título (mín. 3), contenido requerido, búsqueda por texto y filtro por categoría.

POST /api/notas

GET /api/notas

Avanzado

Ejercicio 6.4: Middleware personalizado

Añade tres middlewares a tu API: logger con tiempo, autenticación por API key y rate limiter por IP.

Logger

Imprime cada petición en consola con formato:

[2026-04-29 14:30:00] GET /api/notas - 200 (15ms)

Pista: registra el inicio en req.startTime y al terminar usa el evento res.on('finish').

API key

Header x-api-key obligatorio. Si falta o no coincide con la variable de entorno API_KEY401 Unauthorized.

Rate limiter

Máximo 10 peticiones por minuto por IP. Si se excede →429 Too Many Requests.

Pista: un Map en memoria con req.ip como clave.

🚀

Proyecto: API REST de Lista de Tareas

Construye una API REST completa para una aplicación de tareas concategorías y prioridades. Lista para ser consumida por cualquier frontend.

Modelo de datos

  • id — entero autoincremental
  • titulo — string requerido
  • descripcion — string opcional
  • completada — boolean
  • prioridad — alta / media / baja
  • categoria — string libre
  • fechaCreacion — ISO automático

Funcionalidades

  • • CRUD completo de tareas
  • • Filtrado por estado, prioridad y categoría
  • • Ordenamiento por fecha o prioridad
  • GET /api/tareas/stats con totales
  • • Middleware de logging
  • • Manejo de errores consistente
  • • Códigos HTTP correctos en cada caso

Estructura sugerida

Separa cada responsabilidad en su archivo. No es obligatorio, pero te ahorra dolores cuando la API crezca.

mi-tareas-api/
├── package.json
├── server.js
├── routes/
│   └── tareas.js
├── middleware/
│   ├── logger.js
│   └── errorHandler.js
├── data/
│   └── tareas.js
└── README.md

server.js

Punto de entrada. Configura middleware globales, monta los routers y arranca app.listen().

routes/

Un express.Router() por recurso. Cada archivo registra las rutas de su recurso y exporta el router.

middleware/

Funciones reutilizables: logger, validadores, errorHandler. Importa y enchúfalas con app.use().

Criterios de evaluación

Checklist

  • ☑ CRUD completo funcional
  • ☑ Códigos HTTP correctos (200, 201, 204, 400, 404...)
  • ☑ Validación de datos de entrada
  • ☑ Filtrado por estado, prioridad y categoría
  • ☑ Ordenamiento por fecha o prioridad
  • ☑ Endpoint /stats con totales
  • ☑ Al menos un middleware personalizado
  • ☑ Manejo de errores: nunca crashea el servidor

Bonus

  • ⭐ Persistir en un archivo JSON entre reinicios
  • ⭐ Variables de entorno con dotenv
  • ⭐ Documentación con Swagger / OpenAPI
  • ⭐ Colección Postman o archivo .http incluidos
  • ⭐ CORS configurado para tu portafolio
  • ⭐ Despliegue en Render, Railway o Fly.io
  • ⭐ Tests con Jest o Vitest + Supertest
🔌

Ya hablas el idioma del backend

HTTP, REST y Express son la base sobre la que se construyen las APIs modernas. En la próxima clase añadimos la pieza que faltaba:bases de datos, autenticación real con JWT y despliegue en producción.

Fragua Tech — Clase 6