🚨 El problema: cuando el frontend y el backend viven pegados

Si llevas un tiempo desarrollando aplicaciones web, es probable que hayas empezado con proyectos donde el servidor genera el HTML directamente y lo manda al navegador. Funciona, pero escala mal. ¿Qué pasa cuando el equipo de front y el de back quieren trabajar por separado? ¿O cuando la misma API necesita servir a una app móvil además de a la web?

Aquí es donde aparece la arquitectura SPA desacoplada: el frontend es una aplicación completamente independiente que vive en el navegador, y el backend es una API que solo habla en JSON. Cada uno puede cambiar, escalar o desplegarse sin tocar al otro.

En este post vamos a construir eso desde cero: una aplicación en Angular que consume una API REST en Express + TypeScript, con autenticación JWT y una arquitectura de código limpia y organizada por capas.


🧠 Conceptos clave antes de empezar

Antes de entrar en materia, repasemos los conceptos que necesitas tener claros:

¿Qué es una SPA?

Una Single Page Application es una aplicación web que carga una sola vez en el navegador y luego navega entre vistas sin recargar la página. Angular, React y Vue son los frameworks más populares para construirlas.

¿Qué es una API REST?

Es un servidor que expone datos a través de URLs (endpoints) usando los verbos HTTP: GET para leer, POST para crear, PUT para actualizar y DELETE para borrar. Devuelve siempre JSON.

¿Qué es la arquitectura desacoplada?

Que el frontend y el backend son dos proyectos separados que se comunican únicamente a través de la API. El navegador no sabe nada del servidor; el servidor no sabe nada del navegador.

¿Qué es JWT?

JSON Web Token es una cadena firmada criptográficamente que el cliente guarda y adjunta a cada petición para demostrar que está autorizado. El servidor verifica la firma, y si es válida, responde con los datos.

¿Por qué capas en el frontend?

Separar el código en capas (modelos, repositorios, servicios, componentes) hace que cada pieza tenga una responsabilidad única. Los componentes solo pintan. Los servicios contienen la lógica. Los repositorios hacen las llamadas HTTP. Así el código es fácil de mantener y de probar.


🏗️ Lo que vamos a construir

Aplicacion funcionando
https://flic.kr/p/2sfZbjT

La aplicación tiene:

  • Una tabla de productos con búsqueda, panel de detalle y CRUD completo (crear, editar, borrar).
  • Un API Tester integrado que permite lanzar peticiones a la API y ver en tiempo real las cabeceras enviadas, incluyendo el token JWT.
  • Una API REST en Express + TypeScript con datos simulados en memoria.
  • Autenticación JWT automática: la app obtiene el token al arrancar y lo adjunta a todas las peticiones sin que el usuario tenga que hacer nada.

🛠️ Paso a paso

📦 Paso 1 — Requisitos previos

Necesitas tener instalados:

  • Node.js v18 o superior
  • Angular CLI: npm install -g @angular/cli

⚡ Paso 2 — Crear el proyecto Angular

ng new spa-rest-demo --routing=true --style=css --standalone=false
cd spa-rest-demo
npm install bootstrap

En angular.json, añadimos Bootstrap al array de estilos antes de nuestro CSS propio para que nuestro tema siempre lo sobreescriba:

"styles": [
  "node_modules/bootstrap/dist/css/bootstrap.min.css",
  "src/styles.css"
]
Estructura de carpetas del proyecto
https://flic.kr/p/2sfXSth

🗂️ Paso 3 — La arquitectura de carpetas

La clave del proyecto está en cómo organizamos src/app/. Cada carpeta tiene una responsabilidad única:

src/app/
├── models/          → interfaces TypeScript (Product, ApiResponse…)
├── repositories/    → llamadas HTTP puras (sin lógica de negocio)
├── services/        → lógica de negocio y estado reactivo
├── interceptors/    → añade el JWT a cada petición automáticamente
└── components/      → lo que ve el usuario

Esta separación es deliberada. Si mañana cambias de REST a GraphQL, solo tocas los repositorios. Si cambias cómo se guarda el estado, solo tocas los servicios. Los componentes no se enteran.

🚀 Paso 4 — La API REST con Express y TypeScript

Creamos la carpeta api/ con su propio package.json:

mkdir api && cd api
npm init -y
npm install express cors jsonwebtoken
npm install -D typescript ts-node nodemon @types/express @types/cors @types/jsonwebtoken @types/node

La estructura de la API es igual de limpia:

api/src/
├── data/mock.ts      → array de productos en memoria
├── auth/auth.ts      → secreto JWT, middleware verifyToken, ruta /auth/token
├── routes/products.ts → los endpoints del CRUD
└── server.ts         → Express, CORS, montaje de rutas

El JWT más sencillo posible: al arrancar el servidor se firma un token genérico. La ruta GET /api/auth/token lo entrega. El middleware verifyToken bloquea cualquier petición a /api/products que no lo lleve. Sin usuarios, sin base de datos, sin registro. Solo el mecanismo.

// El token se crea una vez, al arrancar el servidor
export const DEMO_TOKEN = jwt.sign(
  { app: 'spa-rest-demo' },
  JWT_SECRET,
  { expiresIn: '7d' }
);
Terminales corriendo con los endpoints
https://flic.kr/p/2sfX61g

🔑 Paso 5 — El interceptor HTTP en Angular

Este es uno de los conceptos más potentes de Angular. En lugar de añadir el token manualmente en cada llamada, creamos un interceptor que lo hace solo, de forma transparente:

intercept(req, next) {
  const token = this.auth.getToken();
  if (!token) return next.handle(req);
  return next.handle(
    req.clone({ setHeaders: { Authorization: `Bearer ${token}` } })
  );
}

Y para que el token esté disponible antes de la primera petición, usamos APP_INITIALIZER: Angular lo ejecuta al arrancar la app y espera a que termine antes de renderizar nada.

🔄 Paso 6 — El flujo completo

diagrama
https://flic.kr/p/2sfX6WQ
  1. La app arranca y pide GET /api/auth/token (sin token, es pública).
  2. El token se guarda en memoria.
  3. Cualquier llamada posterior pasa por el interceptor, que añade Authorization: Bearer ...
  4. La API verifica la firma y responde con los datos.

Puedes verlo en vivo en el API Tester de la propia aplicación: al hacer cualquier petición, el panel de respuesta muestra las cabeceras enviadas, incluyendo el JWT.

API Headers
https://flic.kr/p/2sfYnBU

▶️ Paso 7 — Arrancar el proyecto

Necesitas dos terminales abiertas a la vez:

# Terminal 1 — API Express
cd api && npm run dev
# → http://localhost:3000

# Terminal 2 — Angular
ng serve --open
# → http://localhost:4200

💡 Recomendaciones

  • Empieza por la API antes de tocar el frontend. Comprueba todos los endpoints antes de escribir una sola línea de Angular.
  • Centraliza los colores en variables CSS (:root { --primary: ... }). Cambiar el tema entero se convierte en editar un único bloque.
  • No mezcles responsabilidades: si un componente está haciendo llamadas HTTP directamente, algo está mal. Eso es trabajo del repositorio.
  • Para proyectos reales, el JWT debería incluir datos del usuario y tener un tiempo de expiración corto con sistema de refresh token. Lo que hemos hecho aquí es suficiente para entender el mecanismo, no para producción.

💬 Preguntas para el debate

  • ¿Tiene sentido usar Angular para una app tan pequeña, o sería suficiente con Fetch API + HTML vanilla?
  • ¿Qué ventajas e inconvenientes tiene almacenar el JWT en memoria frente a guardarlo en localStorage o en una cookie httpOnly?
  • Si la API necesita servir también a una app móvil, ¿cambiaría algo en el diseño?
  • ¿Cuándo dejaría de tener sentido la arquitectura desacoplada y sería mejor un enfoque server-side rendering (SSR)?

Todo el código está disponible en GitHub: Repositorio GitHub


📚 Recursos

Angular:

Express + JWT:

Otros:


Autor: Rubén Gómez-Lobo Fuentes
Máster: Desarrollo Full Stack + Arquitecturas Cloud
Centro: Tajamar Tech
Año académico: 2025-2026
Recursos: Repositorio GitHub

Leave a Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.