Un starter completo y profesional para monorepos TypeScript con Angular 21 + Express.js + PostgreSQL, con backend partido en microservicios (gateway + api) y rotación de refresh con detección de reuso.
Monorepo Nx listo para producción que incluye autenticación JWT con rotación de refresh, gateway de seguridad con EdDSA, gestión de usuarios, sistema de permisos basado en roles, internacionalización y Docker. Pensado para arrancar proyectos SaaS y multi-servicio sin tener que rehacer la capa de auth.
- Frontend: Angular 21 con standalone components, Signals API y control flow nativo (
@if/@for/@switch) - Gateway: Express 5 +
http-proxy-middleware— único servicio expuesto a Internet, gestiona tokens y CORS - API: Express 5 + Sequelize, vive en red privada, expone CRUDs y endpoints
/internal/*para el gateway - Base de datos: PostgreSQL 16
- Monorepo: Nx 22 para gestión eficiente
- Build System: esbuild (backend) + Vite (frontend)
- Containerización: Docker + Docker Compose con redes
edge-networkeinternal-network - UI: Bootstrap 5 + NgBootstrap
- i18n: Transloco (Español/Valenciano/Inglés)
🔐 Autenticación & Seguridad — ver docs/SECURITY.md
- Arquitectura de microservicios: gateway público + api privado. El api nunca habla con el cliente directamente; el gateway firma un JWT interno EdDSA antes de proxiar
- JWT del cliente con dos secretos separados (
JWT_ACCESS_SECRET,JWT_REFRESH_SECRET), claimstypyjti - Rotación de refresh con detección de reuso: tabla
refresh_token_familyrevoca la familia completa si una cookie ya rotada se vuelve a presentar - JWT interno Ed25519 entre gateway y api: el gateway tiene la clave privada (firma), el api sólo la pública (verifica). Privilegio separado: un api comprometido no puede emitir tokens
- Sistema de permisos basado en roles (
ADMIN,WRITE_SOME_ENTITY,READ_SOME_ENTITY), guards Angular y middlewarerequireInternalAuthpor scope - Interceptores HTTP automáticos
- Hashing seguro de contraseñas con bcrypt
Antes del primer arranque generá las claves Ed25519 y los secretos JWT siguiendo docs/SECURITY.md.
- Soporte completo para múltiples idiomas
- Cambio dinámico de idioma
- Persistencia de preferencias
- Traducciones completas de la UI
┌──────────┐ cookie+Authorization ┌──────────┐ X-Internal-Auth (EdDSA) ┌─────┐
│ Cliente │ ─────────────────────────▶│ Gateway │ ──────────────────────────▶ │ API │
└──────────┘ └──────────┘ └─────┘
(público) (privada)
:3100 :3200
- Patrón Controller-Service-Repository en el api
- DTOs compartidos entre frontend y backend en
libs/rest-dto - Contrato interno gateway↔api en
libs/internal-auth(Ed25519 + scopes) - Middleware de autenticación centralizado (
requireInternalAuthpor scope) - Manejo de errores unificado (
HttpResponser,sequelizeErrorMiddleware) - Soft deletes en todas las entidades (
deleted,createdAt,updatedAt,deletedAt)
# Verificar versiones
node --version # >= 22.12.0
npm --version # >= 10.9.0
docker --version
docker compose version# 1. Clonar el repositorio
git clone https://github.com/dherrero/fullstack-starter.git
cd fullstack-starter
# 2. Instalar dependencias
npm install
# 3. Configurar variables de entorno (incluye claves Ed25519 — ver docs/SECURITY.md)
cp .env.example .env
# Editar .env con tus configuraciones y claves generadas
# 4. Iniciar desarrollo
npm run dev- Frontend: http://localhost:4200
- Gateway (público): http://localhost:3100/api/v1/
- API (privado): http://localhost:3200 (sólo accesible vía gateway en docker)
- Base de datos: localhost:5432
Email: test@local.com
Contraseña: 123456
# Desarrollo completo (recomendado)
npm run dev # DB + API + Gateway + Frontend en paralelo
# Desarrollo por pasos
npm run dev:db # Sólo base de datos
npm run dev:api # Sólo api (espera DB)
npm run dev:gateway # Sólo gateway (espera api)
npm run dev:front # Sólo frontend (espera gateway)
# Comandos individuales
npm run start:front # Iniciar frontend
npm run start:api # Iniciar api
npm run start:gateway # Iniciar gateway
npm run start:all # Iniciar los tres serviciosnpm run dev:db:down # Detener base de datos
npm run dev:db:clean # Limpiar volúmenes de DB# Construcción
npm run build:front # Construir frontend
npm run build:api # Construir api
npm run build:gateway # Construir gateway
npm run build # Construir los tres
# Docker
npm run docker:up # Levantar el stack completonpm run test # Todo (front excluido, usa su config)
npm run test:front # Tests del front
npm run test:api # Tests del api
npm run test:gateway # Tests del gateway
npm run test:internal-auth # Tests de la lib de auth interno
npm run test:coverage # Coberturanx-fullstack-starter/
├── apps/
│ ├── front/ # Aplicación Angular 21
│ │ ├── src/app/
│ │ │ ├── components/ # Componentes reutilizables
│ │ │ ├── pages/ # Páginas (home, login)
│ │ │ ├── libs/auth/ # Módulo de autenticación (service, guards)
│ │ │ └── services/ # Servicios de negocio
│ │ └── src/assets/i18n/ # Archivos de traducción
│ ├── gateway/ # Servicio público (Express + http-proxy-middleware)
│ │ └── src/
│ │ ├── controllers/ # auth.controller (login/logout)
│ │ ├── middleware/ # hasPermission, refresh rotation
│ │ ├── services/ # tokenService (firma JWT cliente)
│ │ ├── clients/ # api.client (gateway → api)
│ │ └── routes/ # auth, health, proxy
│ └── api/ # Servicio privado (Express + Sequelize)
│ └── src/
│ ├── controllers/ # internal-auth, refresh-lifecycle, user-crud
│ ├── services/ # AbstractCrudService, refresh-token-family
│ ├── models/ # Sequelize: User, RefreshTokenFamily
│ ├── routes/ # /internal/* + /v1/*
│ └── adapters/ # db, http
├── libs/
│ ├── rest-dto/ # DTOs compartidos front ↔ back
│ └── internal-auth/ # JWT EdDSA + requireInternalAuth middleware
├── db/ # Migraciones SQL (10.user, 20.refresh_token_family)
├── nginx/ # Configuración Nginx (front en prod)
├── docs/ # Documentación (SECURITY.md, etc.)
└── compose.yaml # Docker Compose con redes split
Este proyecto está configurado para trabajar de forma óptima con Claude Code, el agente de codificación de Anthropic. Incluye un sistema de agentes especializados que permite implementar funcionalidades completas de extremo a extremo de forma autónoma, respetando todas las convenciones del proyecto sin que sea necesario recordárselas.
.claude/
├── agents/ # Subagentes especializados
│ ├── database-specialist.md # Base de datos (PostgreSQL, migraciones, índices)
│ ├── backend-developer.md # Backend (Express, Sequelize, servicios, JWT)
│ ├── frontend-developer.md # Frontend (Angular, componentes, formularios)
│ └── qa-engineer.md # Control de calidad (tests, linting, cobertura)
├── skills/ # Skills invocables
│ ├── angular-developer.md # Directrices oficiales de Angular (fuente Google)
│ └── start-agile.md # Integración con kanban Leantime
└── settings.local.json # Permisos y lista de operaciones permitidas/denegadas
El fichero CLAUDE.md de la raíz actúa como orquestador principal: recibe la petición, la descompone por capas y delega en cada subagente respetando el orden de dependencias.
Petición del usuario
↓
[CLAUDE.md] Orquestador
↓
┌──────┬──────────┬──────────┐
↓ ↓ ↓ ↓
DB Backend Frontend QA
↓ ↓ ↓ ↓
└──────┴──────────┴──────────┘
↓
Informe por capa al usuario
El orden de ejecución respeta las dependencias: base de datos → backend → frontend → QA.
Especialista en diseño de esquemas PostgreSQL y MongoDB, migraciones sin downtime, indexación y optimización de consultas.
- Genera ficheros SQL numerados en
db/(nunca auto-sync con Sequelize) - Modelos Sequelize con mapeo
fieldpara columnas lowercase - Soft deletes (
deleted,deletedAt) en todas las entidades - Estrategia de índices: FKs, compuestos, parciales y cubrientes
- Análisis de rendimiento con
EXPLAIN ANALYZE
Especialista en Express + Sequelize siguiendo arquitectura de 4 capas: Routes → Controllers → Services → Models. Trabaja sobre apps/api (lógica de negocio) y apps/gateway (auth público, proxy).
- Patrones
AbstractCrudService/AbstractCrudControllerpara minimizar boilerplate - Todas las respuestas HTTP a través de
HttpResponser(nuncares.json()directo) - Las rutas del api se protegen con
requireInternalAuth({ allowedScopes, requiredPermissions })delibs/internal-auth - Las rutas del gateway se protegen con
hasPermission(Permission.X)del middleware local - Tests unitarios con Vitest, mocks sólo en los límites (DB, HTTP, fetch)
Especialista en Angular siguiendo Clean Architecture y las últimas prácticas oficiales.
- Componentes standalone con
OnPushy Signals API (signal,computed,linkedSignal,resource) inject()para inyección de dependencias — nunca constructor injection- Control flow nativo (
@if,@for,@switch) — sin*ngIfni*ngForen código nuevo - Rutas lazy-loaded con
loadComponent()/loadChildren() - Signal Forms para nuevos formularios (Angular v21+)
- DTOs importados de
libs/rest-dto(fuente única de verdad, nunca redefinidos)
Se ejecuta siempre el último, una vez que todos los agentes de implementación han terminado.
- Compilación TypeScript sin errores (
npx nx run-many -t build) - Linting (
npm run lint) - Tests y cobertura — umbral mínimo del 60% en ficheros nuevos
- Revisión de calidad de tests (aserciones significativas, casos límite cubiertos)
- Revisión de código (SRP, DRY, sin código muerto)
- Checklist de convenciones por capa
- Informe final:
PASS | PASS WITH WARNINGS | FAIL
| Skill | Invocación | Descripción |
|---|---|---|
angular-developer |
/angular-developer |
Carga las directrices oficiales de Angular antes de escribir código. Se invoca automáticamente en el agente de frontend. |
start-agile |
/start-agile |
Activa el modo ágil: crea tickets en Leantime por cada capa y actualiza su estado conforme avanza la implementación. |
Abre Claude Code en la raíz del proyecto y describe en lenguaje natural la funcionalidad que quieres implementar. El orquestador delega en los subagentes correctos y entrega un informe por capas:
"Añade un módulo de gestión de productos con CRUD completo:
tabla products con name, description, price y stock"
Para activar el modo ágil con seguimiento en Leantime:
/start-agile Añade un módulo de gestión de productos con CRUD completo
-
Configuración inicial
git clone https://github.com/dherrero/fullstack-starter.git cd fullstack-starter npm install cp .env.example .env # Generar claves Ed25519 — ver docs/SECURITY.md
-
Desarrollo diario
# Terminal 1: Base de datos npm run dev:db # Terminal 2: API npm run dev:api # Terminal 3: Gateway npm run dev:gateway # Terminal 4: Frontend npm run dev:front
-
Antes de commit
npm run lint npm run test npm run build
- Standalone Components con
OnPush - Control flow nativo (
@if,@for,@switch) — sin*ngIfni*ngForen código nuevo - Services: lógica de negocio inyectada con
inject() - Guards: protección de rutas con
canActivateFn - Interceptors: manejo automático de autenticación
- Reactive Forms: formularios reactivos (Signal Forms a partir de v21+)
- Gateway: emite y verifica el JWT del cliente, inyecta
X-Internal-Auth(EdDSA) en cada request proxiada - API: cualquier ruta
/v1/*o/internal/*se monta detrás derequireInternalAuthcon el scope adecuado - DTOs: tipados compartidos en
libs/rest-dto - Error Handling: middleware unificado de errores Sequelize +
HttpResponser
# Crear feature branch
git checkout -b feat/nueva-funcionalidad
# Desarrollo con commits frecuentes
git add .
git commit -m "feat: añadir nueva funcionalidad"
# Push y PR
git push origin feat/nueva-funcionalidadfeat: nueva funcionalidad
fix: corrección de bug
docs: actualización de documentación
style: cambios de formato
refactor: refactorización de código
test: añadir o modificar tests
chore: tareas de mantenimiento
- Archivos: kebab-case (
user-service.ts) - Clases: PascalCase (
UserService) - Variables: camelCase (
userName) - Constantes: UPPER_SNAKE_CASE (
API_BASE_URL)
# Por proyecto
npm run test:front
npm run test:api
npm run test:gateway
npm run test:internal-auth
# Tests e2e (cuando haya)
npm run e2e:front
# Coverage
npm run test:coverage# Sólo base de datos
docker compose -f docker-compose.db.yml up
# Stack completo
docker compose --env-file .env upnpm run build
docker compose --env-file .env up -dSólo
gatewayyfrontexponen puertos al exterior.apiypostgresdbviven eninternal-networkconinternal: true.
- Crear archivo en
apps/front/src/assets/i18n/nuevo-idioma.json - Actualizar
transloco-loader.service.ts - Añadir opción en
language-switcher.component.ts
// En el componente
this.translocoService.translate('clave.traduccion');<!-- En el template -->
{{ 'clave.traduccion' | transloco }}# Nunca commitees archivos .env
echo ".env" >> .gitignore
# Usa .env.example como plantilla
cp .env.example .env# JWT del cliente (HS256, dos secretos independientes)
JWT_ACCESS_SECRET=... # firma access tokens
JWT_REFRESH_SECRET=... # firma refresh tokens
JWT_EXPIRES_IN=4h
JWT_REFRESH_EXPIRES_IN=8h
# JWT interno gateway → api (Ed25519, asimétrico)
INTERNAL_JWT_PRIVATE_KEY=... # sólo en el gateway
INTERNAL_JWT_PUBLIC_KEY=... # sólo en el apiGeneración de claves en docs/SECURITY.md.
- Lazy loading de rutas
- OnPush change detection
@forcontrack(sustituye atrackByde*ngFor)- Preload strategies
- Connection pooling de Sequelize
- Query optimisation
- Caching strategies
- Compression middleware
CREATE TABLE public.user (
id bigint PRIMARY KEY,
email varchar(150) UNIQUE NOT NULL,
name varchar(150) NOT NULL,
lastname varchar(150),
permissions permission_type[] NOT NULL DEFAULT ARRAY['READ_SOME_ENTITY']::permission_type[],
password varchar(250) NOT NULL,
deleted boolean DEFAULT false,
createdAt timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
updatedAt timestamp,
deletedAt timestamp
);CREATE TABLE public.refresh_token_family (
id bigserial PRIMARY KEY,
user_id bigint NOT NULL REFERENCES public.user(id) ON DELETE CASCADE,
family_id uuid NOT NULL,
jti uuid NOT NULL UNIQUE,
parent_jti uuid,
used boolean NOT NULL DEFAULT false,
revoked_at timestamp,
createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
updatedAt timestamp
);Ver detalles del flujo en docs/SECURITY.md.
ADMIN: Acceso completo al sistemaWRITE_SOME_ENTITY: Ejemplo de permiso de escrituraREAD_SOME_ENTITY: Ejemplo de permiso de lectura
# Database
POSTGRESDB_HOST=localhost
POSTGRESDB_PORT=5432
POSTGRESDB_DATABASE=your_db_name
POSTGRESDB_USER=postgres
POSTGRESDB_PASSWORD=password
# JWT del cliente — dos secretos distintos
JWT_ACCESS_SECRET=dev-access-secret-replace-me
JWT_REFRESH_SECRET=dev-refresh-secret-replace-me
JWT_EXPIRES_IN=4h
JWT_REFRESH_EXPIRES_IN=8h
# JWT interno Ed25519 (PEM con \n literales)
INTERNAL_JWT_PRIVATE_KEY=
INTERNAL_JWT_PUBLIC_KEY=
# Gateway
GATEWAY_PORT=3100
API_BASE_URL=http://api:3200
CORS_ORIGIN=http://localhost:4200
# API
NODE_PORT=3200
NODE_ENV=development
NODE_PRODUCTION=false
HASH_SALT_ROUNDS=10export const env = {
production: false,
// Path relativa para que el proxy del dev server (Vite) la redirija al gateway
api: '/api/v1/',
};# 1. Generar claves Ed25519 y secretos (docs/SECURITY.md)
# 2. Volcar al .env de producción
# 3. Levantar el stack
docker compose --env-file .env up -d --buildNODE_ENV=production
NODE_PRODUCTION=true
POSTGRESDB_HOST=postgresdb
POSTGRESDB_DATABASE=production_db
# Generados con: openssl rand -base64 64
JWT_ACCESS_SECRET=...
JWT_REFRESH_SECRET=...
# Generados con: openssl genpkey -algorithm ed25519 ...
INTERNAL_JWT_PRIVATE_KEY=... # sólo gateway
INTERNAL_JWT_PUBLIC_KEY=... # sólo api
CORS_ORIGIN=https://tu-dominio.com
SERVICE_FQDN_GATEWAY=tu-dominio.com
SERVICE_FQDN_FRONT=app.tu-dominio.com- Fork el proyecto
- Crea una rama para tu feature (
git checkout -b feat/AmazingFeature) - Commit tus cambios (
git commit -m 'feat: Add some AmazingFeature') - Push a la rama (
git push origin feat/AmazingFeature) - Abre un Pull Request
- Sigue las convenciones de código existentes
- Añade tests para nuevas funcionalidades (cobertura mínima 60%)
- Actualiza la documentación si es necesario
- Usa commits descriptivos siguiendo Conventional Commits
- Si tocás auth/tokens, lee
docs/SECURITY.mdantes
Este proyecto está bajo la Licencia MIT. Ver el archivo LICENSE para más detalles.
docker ps # verificar que Docker está corriendo
npm run dev:db:down
npm run dev:db# Gateway (3100)
lsof -ti:3100 | xargs kill
# API (3200)
lsof -ti:3200 | xargs kill
# Front (4200)
lsof -ti:4200 | xargs killLa migración db/20.refresh_token_family.sql no se aplicó. En desarrollo:
npm run dev:db:clean # borra el volumen — recrea con migraciones
npm run dev:dbEn producción, aplicar el SQL manualmente sobre la DB.
- Renombrar el proyecto:
bash scripts/rename.sh mi-saas - Cambiar el branding: textos en
apps/front/src/assets/i18n/, colores enstyles.scss, favicon, logo - Añadir nuevas entidades: tabla SQL en
db/, modelo Sequelize enapps/api/src/models/, servicio que extiendeAbstractCrudService, controlador que extiendeAbstractCrudController, ruta enapps/api/src/routes/protegida porrequireInternalAuth, DTO enlibs/rest-dto - Añadir nuevos micros: copiar el patrón de
apps/apiy declarar la ruta en el proxy delapps/gateway
- SSO/OIDC en el gateway para clientes enterprise (Okta, Azure AD, Auth0)
- SAML para tenants legacy
- SCIM 2.0 para aprovisionamiento masivo
- Multi-tenancy en CRUDs
- Tests e2e completos (Playwright)
- Métricas y observabilidad (OpenTelemetry)
¡Dale una ⭐ en GitHub y compártelo con la comunidad!
¡Disfruta construyendo tu próxima aplicación! 🚀
Hecho con ❤️ por la comunidad
Angular • Express • Nx • PostgreSQL