Saltar a contenido

📚 Unidad ISS-04 · Seeders con Faker — capa 🧠 APRENDER

🛠 Construir este bloque → · ✅ Condiciones de cierre (GATE) · 📝 Evaluación

Capa Página Para qué
🧠 Aprender esta página comprender, explicar y relacionar
🛠 Construir Seeders con Faker ejecutar, programar y verificar
✅ GATE Cierre de la unidad condición para pasar al bloque siguiente

Mapa de correspondencias. Cada fila enlaza el mismo tema en las dos capas de la unidad; los enlaces apuntan a secciones reales del material (anclas de MkDocs, sin acentos).

Tema 🧠 Aprender (esta página) 🛠 Construir (ISS técnico)
Ruta y ficha de la unidad Ruta de aprendizaje · Ficha del ISS Contenido de la unidad
Mapas y estructura Mapa mental · Mapa del backend · Árbol de archivos Contenido de la unidad
Recorrido y comandos Comandos explicados · Recorrido paso a paso Contenido de la unidad
Diagnóstico Diagnóstico Criterios de aceptación
Evaluación y cierre Criterios · Evaluación · GATE Condiciones de cierre · Cierre

🖥 Presentación del ISS

Presentación de la unidad. Diapositivas de ISS-04 — Seeders con Faker (8 diapositivas). Se visualiza aquí, dentro del sitio; no hace falta descargar nada para estudiarla.

⛶ Ver presentación completa ⬇ Archivo editable (.pptx)

8 diapositivas · se visualiza dentro del sitio (archivo editable .pptx como opción secundaria).


🎬 Video explicativo

Recorrido audiovisual de la unidad. Este video explica, bloque por bloque, los seeders del ISS-04: por qué quedan fuera del recorrido HTTP, las guardas de seedClients, Faker y bulkCreate, resolveSeedCounts, runAllSeeders, el archivo real clients.seeder.ts y el GATE.

8:20 min · narración en español · subtítulos activables desde el reproductor.


ISS-04 — Cuaderno de aprendizaje visual

Tema

Seeders con Faker: generar datos falsos por feature con @faker-js/faker y ejecutarlos desde un orquestador externo (SeedersRunner) que recibe la cantidad por entidad.

Fuente técnica autoritativa

Archivo fuente ../manual/05-ISS-04-seeders-faker.md
Estado Solo lectura — este cuaderno no modifica el ISS
Alcance Seeder del feature clients (clients.seeder.ts), conteos por entidad (counts.ts), orquestador externo (index.ts) y el script npm run db:seed

El ISS manda; el cuaderno explica. Todo el contenido técnico del ISS (comandos, rutas, versiones y criterios) aparece aquí íntegro y verbatim más abajo, en la sección Recorrido del ISS, paso a paso. Lo único que añade este cuaderno es el por qué.

Pregunta que responde: ¿de dónde sale cada dato técnico de este cuaderno?

Regla del ISS

Objetivo: datos falsos por feature (Faker) y un orquestador externo que ejecuta todos los seeders enviando la cantidad por entidad. Bloqueado por: ISS-03-A (modelo); recomendado tras ISS-03-E — ambos en ISS-03.

La condición que el propio ISS exige para darse por terminado es doble: el script npm run db:seed debe funcionar y la cantidad debe poder variarse sin tocar el código, ya sea por CLI (npm run db:seed -- --clients=20) o por variable de entorno (SEED_CLIENTS=5). Los seeders escriben con el modelo directamente, fuera del recorrido HTTP.

Pregunta que responde: ¿qué tiene que pasar para poder dar este ISS por bueno?

Cómo leer este cuaderno

Cada concepto se presenta tres veces, desde tres ángulos distintos:

                 CONCEPTO
                    │
        ┌───────────┼───────────┐
        ▼           ▼           ▼
   EXPLICACIÓN    CÓDIGO      VISUAL
        │           │           │
    ¿qué es?    ¿dónde está?  ¿cómo lo
    ¿por qué?   ¿qué hace?     visualizo?
    ¿para qué?  ¿cómo opera?  ¿con qué
                               se relaciona?

Pregunta que responde: ¿cómo está organizado este cuaderno y por qué se enseña todo tres veces?

El recorrido de lectura es siempre el mismo:

EXPLICACIÓN
    ↓
CÓDIGO
    ↓
VISUAL

Pregunta que responde: ¿en qué orden recorro cada concepto dentro del cuaderno?

Y cada cuaderno contiene los mismos seis componentes:

Componente Dónde vive Para qué sirve
Texto todas las secciones entender el por qué
Código Recorrido del ISS ver el qué exacto (verbatim del ISS)
Diagramas Mapa mental, Mapa del backend, Flujos ver el cómo se conecta
Preguntas Evaluación comprobar que entendiste
Evaluación Evaluación practicar y autoevaluarte
GATE GATE saber si puedes pasar al siguiente ISS

Ruta de aprendizaje

Esta ruta es específica de ISS-04: primero el seeder del feature, después el orquestador, y al final el script y la variación de cantidad.

Instalar @faker-js/faker (dev)
    ↓
Escribir clients.seeder.ts (seedClients)
    ↓
Escribir counts.ts (resolveSeedCounts)
    ↓
Escribir index.ts (runAllSeeders)
    ↓
Parchear package.json (db:seed)
    ↓
Ejecutar y variar la cantidad
    ↓
Verificar (GATE)

Pregunta que responde: ¿cuál es el camino concreto que sigo para completar este ISS?

Índice

  1. Ficha del ISS
  2. Mapa mental del ISS
  3. Mapa del backend
  4. Árbol de archivos
  5. Anatomía del código
  6. Flujos
  7. Comandos explicados
  8. Recorrido del ISS, paso a paso
  9. Diagnóstico
  10. Conexión con el resto del curso
  11. Criterios de aceptación
  12. Evaluación
  13. Glosario
  14. GATE

Ficha del ISS

Campo Valor
ISS ISS-04
Título Seeders con Faker (feature + runner)
Objetivo Datos falsos por feature (Faker) y un orquestador externo que ejecuta todos los seeders enviando la cantidad por entidad
Fase Fase I — Business
Tecnología principal @faker-js/faker ^10.6.0 + TypeScript + Sequelize
Depende de ISS-03 — Feature Client (CRUD por capas) (ISS-03-A modelo; recomendado tras ISS-03-E)
Habilita ISS-06 — Feature ProductType
Archivos creados src/features/business/clients/clients.seeder.ts, src/database/seeders/counts.ts, src/database/seeders/index.ts
Archivos parcheados package.json (script db:seed)
Componentes incorporados Seeder del feature clients + SeedCounts/resolveSeedCounts + SeedersRunner (runAllSeeders)
Verificación principal npm run db:seed inserta filas; --clients=20 y SEED_CLIENTS=5 cambian la cantidad
Resultado esperado Datos falsos de clients en la BD, con conteo configurable por default, env o CLI
GATE db:seed idempotente y variedad de cantidad comprobada; npm run dev arranca sin error

Qué implementamos AHORA

  • Un seeder por feature: clients.seeder.ts exporta seedClients(count) y usa Faker para construir filas falsas.
  • Un orquestador externo (src/database/seeders/index.ts, runAllSeeders) que vive fuera del feature, prueba la conexión, hace sync y llama a los seeders.
  • Un archivo de conteos (counts.ts) con SeedCounts, DEFAULT_SEED_COUNTS y resolveSeedCounts.
  • El script db:seed en package.json, con prioridad CLI > env > default.
  • Idempotencia: si ya hay filas en clients, el seeder se omite.

Qué todavía NO implementamos

No se implementa aquí Llega en
Documentación OpenAPI / Swagger UI ISS-05
Segunda feature (product-types) y sus seeders ISS-06
products y asociaciones ISS-07
sales y product-sales (seeders con reglas de negocio replicadas) ISS-08
Capa de seguridad (JWT, bcrypt, AppError) ISS-09
Seeders de users, roles, resources y pivotes ISS-10 … ISS-12

Ojo con la trampa habitual: los seeders no son parte del recorrido HTTP → Controller → Service → Repository. Se ejecutan por consola y escriben con el modelo; por eso no usan DTOs ni services.

Mapa mental del ISS

mindmap
  root((ISS-04<br/>Seeders Faker))
    Objetivo
      Datos falsos por feature
      Orquestador externo
    Seeder del feature
      clients.seeder.ts
      seedClients count
      Idempotente
    Conteos
      SeedCounts
      default 10
      env SEED_CLIENTS
      CLI clients N
    Runner
      database seeders index.ts
      testConnection
      sync
      runAllSeeders
    Script
      db seed
    GATE
      Ejecutar y variar cantidad

Pregunta que responde: ¿de qué trata este ISS y qué piezas lo componen?

Mapa del backend

Esta representación responde siempre a la misma pregunta: ¿en qué parte del backend estamos?

IMPLEMENTADO HASTA ESTE ISS
───────────────────────────

   HTTP → Express App                                        ✅
        ↓
   Routes → Controller → Service → Repository → Model → DTO  ✅ (feature clients, ISS-03)
        ↓
   Sequelize → BD                                            ✅

   🆕 Seeders (fuera del recorrido HTTP)                     ✅ (ISS-04)
      ├── features/business/clients/clients.seeder.ts   (seedClients)
      ├── database/seeders/counts.ts                    (resolveSeedCounts)
      └── database/seeders/index.ts                     (runAllSeeders)

OBJETIVO DE ARQUITECTURA
────────────────────────

   🎯 + documentación OpenAPI y Swagger UI           (ISS-05)
   🎯 + feature product-types                        (ISS-06)
   🎯 + feature products y asociaciones              (ISS-07)
   🎯 + sales y product-sales                        (ISS-08)
   🎯 + capa de seguridad, RBAC y sesión             (ISS-09 … ISS-15)

   ⬜  Nada del stack de autenticación está construido todavía

Pregunta que responde: ¿qué capas del backend existen ya y cuáles siguen siendo objetivo?

Los seeders se añaden al lado de la cadena de capas, no dentro de ella: son la vía de datos por consola, paralela a la vía HTTP.

Árbol de archivos

Leyenda: ★ = creado o trabajado en este ISS · △ = existente parcheado en este ISS.

Estructura antes

src/
├── config/
├── database/
│   ├── db.ts                       (sequelize, testConnection)
│   └── seeders/                    ← todavía no existe
├── features/
│   └── business/
│       └── clients/                (feature completo de ISS-03)
│           ├── client.model.ts
│           ├── clients.controller.ts
│           ├── clients.repository.ts
│           ├── clients.routes.ts
│           ├── clients.service.ts
│           └── dto/
├── routes/
├── shared/
└── server.ts
package.json

Pregunta que responde: ¿qué archivos existían ya antes de empezar este ISS?

Archivos creados / modificados en este ISS

★ src/features/business/clients/clients.seeder.ts   seeder del feature
★ src/database/seeders/counts.ts                    conteos por entidad
★ src/database/seeders/index.ts                     SeedersRunner
△ package.json                                      script db:seed

Pregunta que responde: ¿qué toca exactamente este ISS y con qué rol?

Estructura después

src/
├── config/
├── database/
│   ├── db.ts
│   └── seeders/                    ★ (nuevo)
│       ├── counts.ts               ★
│       └── index.ts                ★
├── features/
│   └── business/
│       └── clients/
│           ├── client.model.ts
│           ├── clients.controller.ts
│           ├── clients.repository.ts
│           ├── clients.routes.ts
│           ├── clients.service.ts
│           ├── clients.seeder.ts   ★
│           └── dto/
├── routes/
├── shared/
└── server.ts
package.json                        △ (script db:seed añadido)

Pregunta que responde: ¿cómo queda el proyecto después de este ISS?

Anatomía del código

Archivo: src/features/business/clients/clients.seeder.ts

Propósito

Generar filas falsas de Client con Faker. Es la pieza que vive dentro del feature y exporta seedClients(count: number): Promise<number>.

Explicación

  • Criterios de entrada — si count <= 0, imprime un aviso y devuelve 0 sin tocar la BD.
  • Idempotencia — consulta Client.count(); si ya hay registros, se omite y devuelve 0. La primera línea de defensa contra duplicar datos.
  • Generación con Faker — faker.person.fullName(), faker.location.streetAddress() y faker.phone.number({ style: "national" }) construyen cada fila; el email añade un sufijo alfanumérico de 6 caracteres para reducir colisiones.
  • Escritura masiva — Client.bulkCreate(rows), no create() fila a fila: una sola sentencia para N filas.
  • Contrato de retorno — devuelve el número de registros insertados (o 0 si se omitió).

Se conecta con

  • Entrada: la llama runAllSeeders (en src/database/seeders/index.ts) con counts.clients.
  • Salida: usa el modelo Client (y por debajo Sequelize) directamente; no pasa por DTOs ni service.

Archivo: src/database/seeders/counts.ts

Propósito

Centralizar cuántos registros genera cada entidad, con una prioridad clara: CLI > env > default.

Explicación

  • Tipo SeedCounts — declara las claves por entidad (clients: number) y deja comentadas las futuras (users, roles, products).
  • DEFAULT_SEED_COUNTS — el valor por defecto (clients: 10).
  • resolveSeedCounts(argv) — parte del default, lo pisa con process.env.SEED_CLIENTS si existe y no está vacío, y luego recorre argv buscando argumentos con forma --clave=valor.
  • Validación implícita — un argumento CLI solo se aplica si key in counts, es decir, si la entidad ya existe en el objeto.

Se conecta con

  • Entrada: la llaman runAllSeeders() (sin argumentos, lee process.argv.slice(2) por defecto).
  • Salida: devuelve un SeedCounts listo para pasar a cada seeder.

Archivo: src/database/seeders/index.ts

Propósito

Ser el orquestador externo: importa los seeders de los features y los ejecuta en orden, precargando la configuración y la conexión.

Explicación

  • Carga de entorno — dotenv.config() antes de tocar la BD, para que las credenciales estén disponibles.
  • Import con efecto secundario — importa client.model para garantizar que el modelo esté registrado en Sequelize antes del sync.
  • runAllSeeders() — resuelve los conteos, los imprime, llama a testConnection() (y lanza si no hay conexión), ejecuta sequelize.sync({ force: false, alter: true }) y luego llama a los seeders en orden padres → hijos.
  • Ejecución directa — el bloque if (require.main === module) permite ejecutarlo con ts-node, cierra Sequelize y sale con código 0 o 1.

Se conecta con

  • Entrada: lo dispara el script db:seed de package.json (ts-node -- src/database/seeders/index.ts).
  • Salida: usa sequelize, testConnection y seedClients.

Flujos

sequenceDiagram
    autonumber
    participant Dev as "Desarrollador"
    participant Npm as "npm run db:seed"
    participant Runner as "runAllSeeders"
    participant DB as "Sequelize + BD"
    participant Seeder as "seedClients"

    Dev->>Npm: ejecuta el script
    Npm->>Runner: ts-node src/database/seeders/index.ts
    Runner->>Runner: resolveSeedCounts()
    Runner->>DB: testConnection()
    DB-->>Runner: true
    Runner->>DB: sync force false alter true
    Runner->>Seeder: seedClients(counts.clients)
    Seeder->>DB: Client.count()
    DB-->>Seeder: 0
    Seeder->>DB: Client.bulkCreate(rows)
    Seeder-->>Runner: count insertado
    Runner-->>Dev: SeedersRunner finalizado

Pregunta que responde: ¿qué ocurre paso a paso cuando ejecuto npm run db:seed?

Comandos explicados

npm install -D @faker-js/faker@^10.6.0

COMANDO
   ↓
npm install -D @faker-js/faker@^10.6.0
   ↓
QUÉ HACE
   Instala Faker como dependencia de desarrollo.
   ↓
POR QUÉ SE NECESITA
   Es la librería que genera nombres, direcciones y teléfonos falsos.
   Va en -D porque solo la usan los scripts de seed, no la API en
   producción.
   ↓
QUÉ CREA O MODIFICA
   package.json (devDependencies) y package-lock.json.
   ↓
RESULTADO ESPERADO
   La versión ^10.6.0 registrada en devDependencies.
   ↓
CÓMO VERIFICARLO
   npm ls @faker-js/faker  (o revisar package.json).

npm run db:seed

COMANDO
   ↓
npm run db:seed
   ↓
QUÉ HACE
   Ejecuta src/database/seeders/index.ts con ts-node.
   ↓
POR QUÉ SE NECESITA
   Es la vía por consola para poblar la BD con datos falsos.
   ↓
QUÉ CREA O MODIFICA
   Filas nuevas en las tablas (clients) si están vacías.
   ↓
RESULTADO ESPERADO
   Mensajes de conteos y "clients: insertados N registro(s) falsos".
   ↓
CÓMO VERIFICARLO
   Volver a ejecutarlo: debe responder "ya hay N registro(s), se omite".

npm run db:seed -- --clients=20

COMANDO
   ↓
npm run db:seed -- --clients=20
   ↓
QUÉ HACE
   Pasa el argumento --clients=20 al script (el `--` separa los args de npm
   de los args del script).
   ↓
POR QUÉ SE NECESITA
   Demuestra que la cantidad se configura por CLI sin editar código.
   ↓
QUÉ CREA O MODIFICA
   Filas nuevas según el conteo recibido (si la tabla estaba vacía).
   ↓
RESULTADO ESPERADO
   Conteo clients = 20 en el log del runner.
   ↓
CÓMO VERIFICARLO
   Ver "📊 Conteos: { clients: 20 }" en la salida.

SEED_CLIENTS=5 npm run db:seed

COMANDO
   ↓
SEED_CLIENTS=5 npm run db:seed
   ↓
QUÉ HACE
   Define la variable de entorno SEED_CLIENTS solo para ese proceso.
   ↓
POR QUÉ SE NECESITA
   Demuestra la segunda vía de configuración (env) del resolver.
   ↓
QUÉ CREA O MODIFICA
   Filas nuevas según el conteo por entorno (si la tabla estaba vacía).
   ↓
RESULTADO ESPERADO
   Conteo clients = 5 en el log del runner.
   ↓
CÓMO VERIFICARLO
   Ver "📊 Conteos: { clients: 5 }" en la salida.

npm run dev

COMANDO
   ↓
npm run dev
   ↓
QUÉ HACE
   Arranca el servidor Express en modo desarrollo.
   ↓
POR QUÉ SE NECESITA
   Es el cierre del ISS: comprobar que los cambios no rompieron la App.
   ↓
QUÉ CREA O MODIFICA
   Nada persistente: solo levanta el proceso del servidor.
   ↓
RESULTADO ESPERADO
   El servidor arranca sin error.
   ↓
CÓMO VERIFICARLO
   Que la consola no muestre excepciones; detenerlo con Ctrl+C.

Recorrido del ISS, paso a paso

A partir de aquí viene el contenido técnico completo del ISS, verbatim: su objetivo, sus criterios y sus bloques de código. Se reproduce sin resumir y sin reformatear; solo se han degradado los encabezados un nivel para que aniden bajo esta sección, y se han reescrito los enlaces relativos hacia ../manual/.

Fase I: Business — ISS-04 — Seeders con Faker (feature + runner)

Contexto mínimo (autosuficiente). Si abres este archivo aislado —o lo llevas a otra IA—, esto es lo que hay que saber antes de ejecutarlo. - Proyecto: app-storelab-express-ii — Express 5 + TypeScript + Sequelize, arquitectura por features, Fase I solo Business (sin auth ni roles). - Recorrido obligatorio de una petición: HTTP → Controller → Service → Repository → Model → Sequelize → BD. Ninguna capa se salta a la siguiente. - Diseño de datos (columnas, tipos, FK RESTRICT, índices, transacciones): ../bd-storelab.md, Fase I — Business. - Capas, convenciones y reglas transversales: 00-contexto.md.

Este ISS
Título Seeders con Faker (feature + runner)
Feature / tabla clients (seeder)
API npm run db:seed
Depende de ISS-03 — Feature Client (CRUD por capas)
Habilita ISS-06 — Feature ProductType

Contenido de este ISS

  • 9.1 Seeder dentro del feature Client
  • 9.2 SeedersRunner + conteos por entidad (database/seeders)

Objetivo: datos falsos por feature (Faker) y un orquestador externo que ejecuta todos los seeders enviando la cantidad por entidad.
Bloqueado por: ISS-03-A (modelo); recomendado tras ISS-03-E — ambos en ISS-03.

Criterios de aceptación (ISS-04) — consolidados

  • [ ] 9.1 Existe features/business/clients/clients.seeder.ts con @faker-js/faker, recibe count, es idempotente
  • [ ] 9.2 Existe database/seeders/index.ts (SeedersRunner) que llama seeders de features
  • [ ] 9.2 Existe database/seeders/counts.ts con cantidad por entidad (default / env / CLI)
  • [ ] Script npm run db:seed funciona
  • [ ] Se puede variar cantidad: npm run db:seed -- --clients=20 o SEED_CLIENTS=5

Diseño

Pieza Ubicación Rol
Seeder del feature src/features/business/clients/clients.seeder.ts Genera filas falsas de Client
Conteos src/database/seeders/counts.ts clients: N (y futuras entidades)
Runner src/database/seeders/index.ts Importa seeders de features y los ejecuta en orden

Los seeders son scripts, no la API. Viven fuera del recorrido HTTP → Controller → Service → Repository: se ejecutan por consola (npm run db:seed) y escriben con el modelo directamente. Por eso no usan los DTOs ni el service, y cuando necesitan una regla del dominio la replican y la documentan (ej. product-sales.seeder.ts recalcula subtotal/total con la misma fórmula que recalculateSaleTotals). Así el código de la API no arrastra dependencias de scripts.


9.1 Seeder dentro del feature Client

Criterios

  • [ ] seedClients(count: number) exportado desde el feature
  • [ ] Usa @faker-js/faker
  • [ ] Si ya hay filas, no duplica
npm install -D @faker-js/faker@^10.6.0
: > src/features/business/clients/clients.seeder.ts
cat >> src/features/business/clients/clients.seeder.ts << 'EOF'
import { faker } from "@faker-js/faker";
import { Client } from "./client.model";

/**
 * Seeder del feature Client (datos falsos con @faker-js/faker).
 * Se invoca desde `src/database/seeders` (SeedersRunner), no desde la App.
 *
 * Idempotente: si ya hay filas, no vuelve a insertar.
 */
export async function seedClients(count: number): Promise<number> {
  if (count <= 0) {
    console.log("⏭️  clients: count=0, se omite");
    return 0;
  }

  const existing = await Client.count();
  if (existing > 0) {
    console.log(`⏭️  clients: ya hay ${existing} registro(s), se omite seeder`);
    return 0;
  }

  const rows = Array.from({ length: count }, (_, i) => ({
    name: faker.person.fullName(),
    address: faker.location.streetAddress(),
    phone: faker.phone.number({ style: "national" }),
    email: `client.${i}.${faker.string.alphanumeric(6)}@example.com`.toLowerCase(),
    password: "Password123!",
    status: "active" as const,
  }));

  await Client.bulkCreate(rows);
  console.log(`✅ clients: insertados ${count} registro(s) falsos`);
  return count;
}
EOF

9.2 SeedersRunner + conteos por entidad (database/seeders)

Criterios

  • [ ] Runner fuera del feature en src/database/seeders/
  • [ ] Cantidad configurable por feature (clients, …)

9.2.1 Conteos

: > src/database/seeders/counts.ts
cat >> src/database/seeders/counts.ts << 'EOF'
/**
 * Cantidad de registros por feature/entidad.
 * Prioridad: CLI (--clients=N) > env (SEED_CLIENTS) > default de este archivo.
 *
 * Cuando agregues features, suma aquí la clave y léela en el runner.
 */
export type SeedCounts = {
  clients: number;
  // users?: number;
  // roles?: number;
  // products?: number;
};

export const DEFAULT_SEED_COUNTS: SeedCounts = {
  clients: 10,
};

export function resolveSeedCounts(argv: string[] = process.argv.slice(2)): SeedCounts {
  const counts: SeedCounts = { ...DEFAULT_SEED_COUNTS };

  const envClients = process.env.SEED_CLIENTS;
  if (envClients !== undefined && envClients !== "") {
    counts.clients = Number(envClients);
  }

  for (const arg of argv) {
    const m = arg.match(/^--([a-zA-Z_]+)=(\d+)$/);
    if (!m) continue;
    const key = m[1] as keyof SeedCounts;
    const value = Number(m[2]);
    if (key in counts) {
      counts[key] = value;
    }
  }

  return counts;
}
EOF

9.2.2 Runner

: > src/database/seeders/index.ts
cat >> src/database/seeders/index.ts << 'EOF'
import dotenv from "dotenv";
import { sequelize, testConnection } from "../db";
import "../../features/business/clients/client.model";
import { seedClients } from "../../features/business/clients/client.seeder";
import { resolveSeedCounts } from "./counts";

dotenv.config();

/**
 * SeedersRunner — ejecuta TODOS los seeders de features.
 *
 * Ubicación: `src/database/seeders/` (orquestación fuera de cada feature).
 * Cada feature exporta su seeder (ej. `features/business/clients/clients.seeder.ts`).
 *
 * Uso:
 *   npm run db:seed
 *   npm run db:seed -- --clients=20
 *   SEED_CLIENTS=5 npm run db:seed
 */
export async function runAllSeeders(): Promise<void> {
  const counts = resolveSeedCounts();
  console.log("🌱 Iniciando SeedersRunner...");
  console.log("📊 Conteos:", counts);

  const ok = await testConnection();
  if (!ok) {
    throw new Error("No hay conexión a la base de datos");
  }

  await sequelize.sync({ force: false, alter: true });

  // Orden: business (padres → hijos)
  await seedClients(counts.clients);

  console.log("🌱 SeedersRunner finalizado");
}

if (require.main === module) {
  runAllSeeders()
    .then(async () => {
      await sequelize.close();
      process.exit(0);
    })
    .catch(async (err) => {
      console.error("❌ Error en seeders:", err);
      await sequelize.close();
      process.exit(1);
    });
}
EOF

PARCHE — package.json ya existe.

Dentro de "scripts", debajo de "dev": "...", añadir la coma al final de dev (si falta) y la clave:

    "db:seed": "ts-node -- src/database/seeders/index.ts"

Fragmento esperado:

  "scripts": {
    "build": "tsc",
    "dev": "nodemon --watch src --ext ts --exec ts-node -- src/server.ts",
    "db:seed": "ts-node -- src/database/seeders/index.ts"
  }

Verificación ISS-04

npm run db:seed
npm run db:seed -- --clients=20
SEED_CLIENTS=5 npm run db:seed

Al agregar otra entidad (patrón):

  1. Archivo nuevo features/.../<plural>.seeder.ts con : > + cat >>.
  2. PARCHE counts.ts: dentro de SeedCounts / defaults, añadir clave (ej. products: 10).
  3. PARCHE database/seeders/index.ts: debajo de await seedClients(...), añadir la llamada al nuevo seeder.

Cierre del ISS

npm run dev

El servidor debe arrancar sin error. Detenerlo con Ctrl+C antes de continuar.

Diagnóstico

Síntoma Causa probable Solución
Cannot find module '@faker-js/faker' Faker no instalado o instalado solo en otro directorio npm install -D @faker-js/faker@^10.6.0
El seeder imprime «ya hay N registro(s), se omite» La tabla clients no está vacía (idempotencia) Es el comportamiento esperado; vaciar la tabla o usar otra base si quieres ver la inserción
No hay conexión a la base de datos Credenciales o motor de BD caídos Revisar .env y que el motor acepte conexiones (ISS-02)
El conteo no cambia con --clients=20 El argumento no llegó al script (falta el -- de npm) Usar npm run db:seed -- --clients=20
SEED_CLIENTS=5 no tiene efecto Se ejecutó sin la variable o el runner leyó otro valor Revisar que la variable se exporte para ese proceso
Error al importar el modelo en el runner Modelo no registrado antes del sync El runner importa client.model a propósito; no borres ese import

Pregunta que responde: si algo falla al sembrar los datos, ¿por dónde empiezo a mirar?

Conexión con el resto del curso

  • Lo habilita: ISS-03 — Feature Client. Sin el modelo Client no hay nada que sembrar.
  • Lo usa: ISS-06 — Feature ProductType y siguientes, que añaden claves a counts.ts y llamadas nuevas en el runner.
  • Patrón que reutiliza: el registry externo. El mismo patrón (counts + index fuera del feature) reaparecerá en ISS-05 para Swagger (src/swagger/index.ts).
  • Regla transversal: los seeders replican y documentan las reglas de negocio que necesiten, en lugar de importar services, para que los scripts no arrastren dependencias de la API.

Pregunta que responde: ¿con qué piezas anteriores y posteriores del curso se conecta este ISS?

Criterios de aceptación

Los del ISS, textuales:

  • [ ] 9.1 Existe features/business/clients/clients.seeder.ts con @faker-js/faker, recibe count, es idempotente
  • [ ] 9.2 Existe database/seeders/index.ts (SeedersRunner) que llama seeders de features
  • [ ] 9.2 Existe database/seeders/counts.ts con cantidad por entidad (default / env / CLI)
  • [ ] Script npm run db:seed funciona
  • [ ] Se puede variar cantidad: npm run db:seed -- --clients=20 o SEED_CLIENTS=5

Criterios internos de cada bloque (también del ISS):

  • [ ] seedClients(count: number) exportado desde el feature
  • [ ] Usa @faker-js/faker
  • [ ] Si ya hay filas, no duplica
  • [ ] Runner fuera del feature en src/database/seeders/
  • [ ] Cantidad configurable por feature (clients, …)

Evaluación

Preguntas de comprensión

  1. ¿Por qué los seeders viven en database/seeders/ y no dentro del recorrido de capas? Porque son scripts de consola, no peticiones HTTP. Se ejecutan con npm run db:seed y escriben directamente con el modelo; usar el service o los DTOs acoplaría la API a los scripts. El SeedersRunner se coloca fuera de los features para orquestar a todos sin que ninguno dependa de otro.

  2. ¿Qué significa que seedClients sea idempotente y cómo lo logra? Significa que ejecutarlo varias veces no duplica datos. Lo logra consultando primero Client.count(): si ya hay filas, imprime un aviso y devuelve 0 sin insertar.

  3. ¿Cuál es la prioridad de configuración de la cantidad y por qué ese orden? CLI > env > default. El argumento explícito (--clients=20) es la intención más inmediata y concreta; la variable de entorno sirve para entornos (por ejemplo CI); y el default (10) garantiza que el comando funcione sin configuración.

  4. ¿Qué hace sequelize.sync({ force: false, alter: true }) en el runner y por qué es importante? Sincroniza los modelos con la BD sin borrar (force: false) y ajustando la estructura (alter: true). Es importante para que las tablas existan antes de insertar filas, incluso en una base recién creada.

  5. ¿Por qué el runner comprueba testConnection() antes de sembrar? Para fallar con un mensaje claro («No hay conexión a la base de datos») en lugar de dejar que el error salga disfrazado como un fallo de Faker o de bulkCreate. Diagnóstico temprano.

  6. ¿Qué pasaría si el runner no importara client.model? El modelo podría no estar registrado en Sequelize al ejecutar sync, y la tabla clients no se crearía o el seeder fallaría al usarlo. Ese import con efecto secundario es deliberado.

  7. El comando npm run db:seed -- --clients=20 lleva -- dos veces. ¿Por qué? El primer -- es el que npm usa para separar sus propias banderas de las del script; lo que va después llega a process.argv y lo interpreta resolveSeedCounts.

Ejercicios

Ejercicio 1 — Traza el resolver. Con DEFAULT_SEED_COUNTS = { clients: 10 }, ejecuta mentalmente resolveSeedCounts(["--clients=7", "--products=3"]). ¿Qué devuelve y por qué se ignora --products?

Respuesta razonada Devuelve `{ clients: 7 }`. El default es `10`, no hay `SEED_CLIENTS`, y el bucle aplica `--clients=7` porque `clients in counts`. `--products=3` **se ignora** porque la clave `products` todavía no existe en `SeedCounts` (está comentada); el resolver solo acepta claves ya declaradas. Ese detalle evita que un typo en CLI pase desapercibido.

Ejercicio 2 — Sembrar dos veces. Explica qué imprime el segundo npm run db:seed consecutivo y en qué línea del seeder se decide.

Respuesta razonada El segundo arranque vuelve a resolver conteos, prueba conexión y hace `sync`, pero al llegar a `seedClients` la consulta `Client.count()` devuelve un número mayor que 0, así que imprime «clients: ya hay N registro(s), se omite seeder» y devuelve `0`. No hay inserción. La decisión está en el bloque `if (existing > 0)` del seeder.

Ejercicio 3 — Añadir una entidad (diseño). Siguiendo el patrón del ISS, enumera los tres pasos que tendrías que dar para sembrar una entidad nueva, sin escribir el código.

Respuesta razonada 1. Crear el archivo del seeder dentro del feature (`features/.../.seeder.ts`) exportando su función con `count`. 2. **Parchear** `counts.ts`: añadir la clave nueva (por ejemplo `products: 10`) dentro de `SeedCounts` y en `DEFAULT_SEED_COUNTS`. 3. **Parchear** `database/seeders/index.ts`: añadir la llamada al seeder nuevo debajo de la anterior, respetando el orden `padres → hijos`. Ese es exactamente el patrón que documenta el ISS para las entidades futuras.

Glosario

Término Significado
Seeder Script que puebla la BD con datos (en este ISS, falsos).
Faker Librería @faker-js/faker que genera datos aleatorios realistas.
SeedersRunner Orquestador externo (runAllSeeders) que ejecuta todos los seeders.
Idempotencia Propiedad de una operación repetible que no cambia el resultado al repetirse.
bulkCreate Inserción masiva de varias filas en una sola operación de Sequelize.
count Número de registros; base de la comprobación de idempotencia.
CLI > env > default Orden de prioridad con que se resuelve la cantidad a sembrar.

GATE

Para cerrar el ISS-04, ejecuta en este orden:

npm run db:seed
npm run db:seed -- --clients=20
SEED_CLIENTS=5 npm run db:seed

Resultado esperado: el runner imprime los conteos y el resultado de cada seeder; la primera ejecución inserta y las siguientes respetan la idempotencia y la cantidad indicada.

Y el cierre del ISS, comprobar que la App sigue en pie:

npm run dev

Resultado esperado: el servidor arranca sin error. Detenerlo con Ctrl+C antes de continuar.

Checklist de cierre:

  • [ ] clients.seeder.ts con Faker, count e idempotencia
  • [ ] database/seeders/index.ts (SeedersRunner) llama a los seeders de features
  • [ ] database/seeders/counts.ts con default / env / CLI
  • [ ] npm run db:seed funciona
  • [ ] npm run db:seed -- --clients=20 y SEED_CLIENTS=5 npm run db:seed cambian la cantidad
  • [ ] npm run dev arranca sin error

Con todos en verde, el ISS-04 está cumplido y puedes pasar al ISS-05 — Swagger / OpenAPI, donde el mismo patrón de registry externo se usa para montar la documentación.


Navegación de la ruta: ← ISS-03 · 🛠 Construir · ↑ Ruta Express · → ISS-04 · 🛠 Construir