🛠 Unidad ISS-04 · Seeders con Faker — capa 🛠 CONSTRUIR
🧠 Comprender este bloque → · 📝 Evaluación · ✅ GATE de la unidad
Capa Página Para qué 🧠 Aprender Seeders con Faker comprender, explicar y relacionar 🛠 Construir esta página ejecutar, programar y verificar ✅ GATE Condiciones de cierre condición para pasar al bloque siguiente Esta es la guía ejecutable. Sigue los pasos en orden. El cuerpo de abajo es el ISS técnico verbatim: comandos, rutas, versiones, PARCHEs, verificaciones y criterios, sin simplificar ni modernizar.
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, FKRESTRICT, í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:seedDepende 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.tscon@faker-js/faker, recibecount, es idempotente - [ ] 9.2 Existe
database/seeders/index.ts(SeedersRunner) que llama seeders de features - [ ] 9.2 Existe
database/seeders/counts.tscon cantidad por entidad (default / env / CLI) - [ ] Script
npm run db:seedfunciona - [ ] Se puede variar cantidad:
npm run db:seed -- --clients=20oSEED_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.tsrecalculasubtotal/totalcon la misma fórmula querecalculateSaleTotals). 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
: > 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:
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
Al agregar otra entidad (patrón):
- Archivo nuevo
features/.../<plural>.seeder.tscon: >+cat >>. - PARCHE
counts.ts: dentro deSeedCounts/ defaults, añadir clave (ej.products: 10). - PARCHE
database/seeders/index.ts: debajo deawait seedClients(...), añadir la llamada al nuevo seeder.
Cierre del ISS
El servidor debe arrancar sin error. Detenerlo con Ctrl+C antes de continuar.
✅ GATE de la unidad ISS-04 — este bloque no añade ningún criterio nuevo.
Las condiciones de cierre son exactamente las que define el ISS técnico de esta misma página:
- 📋 Criterios de aceptación → Criterios de aceptación
- 🧩 Cierre de la unidad → Cierre de la unidad
- 🧠 Autoevaluación → Evaluación del cuaderno
Con el GATE en verde queda habilitado ISS-05 · Swagger / OpenAPI.
Navegación de la ruta: ← ISS-04 · 🧠 Aprender · ↑ Ruta Express · → ISS-05 · 🧠 Aprender