Skip to content

Latest commit

 

History

History
402 lines (302 loc) · 9.74 KB

File metadata and controls

402 lines (302 loc) · 9.74 KB

🗄️ Base de Datos con TypeORM

📋 Configuración

Variables de Entorno (.env)

# Base de Datos MySQL
DB_DRIVER=mysql
DB_HOST=localhost
DB_PORT=3306
DB_USERNAME=tu_usuario
DB_PASSWORD=tu_password
DB_DATABASE=SDC_SHORT_LINK
DB_DEBUG=false
DB_SYNC=false

⚠️ IMPORTANTE: DB_SYNC debe ser false en producción. Usar migraciones para cambios en esquema.


🚀 Uso Rápido

1. Ejecutar Migraciones

# Ejecutar todas las migraciones pendientes
npm run migration:run

# Revertir última migración
npm run migration:revert

2. Generar Nueva Migración (Automática)

# TypeORM detecta cambios en entidades y genera migración
# El path se especifica como argumento posicional al final
npm run migration:generate ./src/migration/NombreDeLaMigracion

3. Crear Migración Manual

# Crear archivo de migración vacío
# El path se especifica como argumento posicional
npm run migration:create ./src/migration/NombreDeLaMigracion

📁 Estructura de Archivos

src/
├── entity/
│   └── ShortLink.entity.ts      # Entidad con decoradores TypeORM
├── repositories/
│   └── ShortLink.repository.ts  # Lógica de acceso a datos
├── migration/
│   └── 1732000000000-CreateShortLinksTable.ts  # Migración inicial
├── data-source.ts               # Configuración DataSource
└── index.ts                     # Bootstrap con inicialización de BD

🏗️ Entidad ShortLink

Decoradores Principales

@Entity('short_links')              // Nombre de tabla
@Index('idx_codigo', ['codigo'])    // Índice para búsquedas rápidas

@PrimaryGeneratedColumn('uuid')     // ID autogenerado UUID
@Column({ type: 'varchar', length: 100 })  // Columna de texto
@Column({ type: 'enum', enum: ShortLinkStatus })  // Enum
@Column({ type: 'json' })           // Columna JSON (etiquetas, historial)
@CreateDateColumn()                 // Auto-timestamp creación
@UpdateDateColumn()                 // Auto-timestamp actualización

Mapeo de Nombres (camelCase ↔ snake_case)

Propiedad TypeScript Columna MySQL
urlCorta url_corta
urlDestino url_destino
fechaCreacion fecha_creacion
fechaUltimaActualizacion fecha_ultima_actualizacion
creadoPor creado_por
actualizadoPor actualizado_por
historialCambios historial_cambios
totalClicks total_clicks
fechaUltimoClick fecha_ultimo_click

🔧 Repositorio ShortLink

Métodos Principales

CRUD Básico

// Crear
const link = await shortLinkRepository.create({
  codigo: 'promo2025',
  urlCorta: 'https://lnk.example.com/promo2025',
  proyecto: 'Marketing',
  creadoPor: 'admin'
});

// Buscar por ID
const link = await shortLinkRepository.findById('uuid-aqui');

// Buscar por código
const link = await shortLinkRepository.findByCodigo('promo2025');

// Actualizar
const updated = await shortLinkRepository.update('uuid-aqui', {
  urlDestino: 'https://nueva-url.com',
  estado: ShortLinkStatus.ACTIVO
});

// Eliminar
await shortLinkRepository.delete('uuid-aqui');

Búsquedas Avanzadas

// Listar con filtros
const links = await shortLinkRepository.findAll({
  proyecto: 'Marketing',
  estado: ShortLinkStatus.ACTIVO
});

// Buscar por proyecto
const links = await shortLinkRepository.findByProyecto('Marketing');

// Buscar por estado
const links = await shortLinkRepository.findByEstado(ShortLinkStatus.RESERVADO_SIN_DESTINO);

// Buscar por etiqueta
const links = await shortLinkRepository.findByEtiqueta('promo');

// Búsqueda flexible
const links = await shortLinkRepository.search('navidad');

Métricas

// Incrementar clics (operación atómica)
await shortLinkRepository.incrementClicks('uuid-aqui');

// Enlaces más populares
const top10 = await shortLinkRepository.findMostPopular(10);

// Contar por estado
const count = await shortLinkRepository.countByEstado(ShortLinkStatus.ACTIVO);

// Contar por proyecto
const count = await shortLinkRepository.countByProyecto('Marketing');

// Total de clics
const totalClicks = await shortLinkRepository.getTotalClicks();

Validaciones

// Verificar si código existe
const exists = await shortLinkRepository.existsByCodigo('promo2025');

// Enlaces reservados sin destino
const reservados = await shortLinkRepository.findReservados();

🔄 Migraciones

Migración Inicial: CreateShortLinksTable

Crea la tabla short_links con:

  • ✅ 15 columnas con tipos apropiados
  • ✅ Índices en codigo, proyecto, estado, fecha_creacion
  • ✅ Enums para estado
  • ✅ JSON para etiquetas e historial
  • ✅ Timestamps automáticos

Ejecutar Migración

# Desarrollo
npm run migration:run

# Producción (después de compilar)
npm run build
npm run migration:run:prod

Verificar Estado

# Conectar a MySQL
mysql -h localhost -u sdc -p SDC_SHORT_LINK

# Ver tablas
SHOW TABLES;

# Ver estructura de short_links
DESCRIBE short_links;

# Ver índices
SHOW INDEX FROM short_links;

# Ver migraciones ejecutadas
SELECT * FROM migrations;

🧪 Testing Manual

Crear Enlace de Prueba

curl -X POST http://localhost:4300/v1/short-links \
  -H "X-API-Key: dev-key-123" \
  -H "Content-Type: application/json" \
  -d '{
    "proyecto": "Test",
    "creadoPor": "admin",
    "urlDestino": "https://google.com"
  }'

Verificar en Base de Datos

-- Ver todos los enlaces
SELECT * FROM short_links;

-- Ver enlaces activos
SELECT codigo, url_corta, url_destino, total_clicks 
FROM short_links 
WHERE estado = 'ACTIVO';

-- Ver enlaces por proyecto
SELECT * FROM short_links WHERE proyecto = 'Marketing';

-- Ver estadísticas
SELECT 
    proyecto,
    COUNT(*) as total_enlaces,
    SUM(total_clicks) as total_clics
FROM short_links
GROUP BY proyecto;

🐛 Troubleshooting

Error: "Connection refused"

Causa: MySQL no está corriendo o credenciales incorrectas

Solución:

# Verificar que MySQL esté corriendo
# Windows:
Get-Service mysql* | Select Name, Status

# Linux:
sudo systemctl status mysql

# Verificar conexión
mysql -h localhost -u sdc -p

Error: "Table doesn't exist"

Causa: Migraciones no ejecutadas

Solución:

npm run migration:run

Error: "Cannot find module 'typeorm'"

Causa: Dependencias no instaladas

Solución:

npm install

Error: "Duplicate entry for key 'codigo'"

Causa: Código ya existe en BD

Solución: El controlador maneja esto automáticamente generando un código diferente. Si persiste, verificar lógica en resolveShortCode().


📊 Esquema de Base de Datos

Tabla: short_links

Columna Tipo Null Key Default Comentario
id VARCHAR(36) NO PRI UUID() ID único
codigo VARCHAR(100) NO UNI Código corto
url_corta VARCHAR(255) NO URL completa
url_destino TEXT YES NULL URL destino
estado ENUM NO MUL RESERVADO_SIN_DESTINO Estado
proyecto VARCHAR(100) NO MUL Proyecto
modulo VARCHAR(100) YES NULL Módulo
etiquetas JSON YES NULL Etiquetas
fecha_creacion TIMESTAMP NO MUL CURRENT_TIMESTAMP Creado
fecha_ultima_actualizacion TIMESTAMP NO CURRENT_TIMESTAMP Actualizado
creado_por VARCHAR(100) NO Usuario creador
actualizado_por VARCHAR(100) NO Usuario actualizador
historial_cambios JSON NO '[]' Historial
total_clicks INT UNSIGNED NO 0 Contador
fecha_ultimo_click TIMESTAMP YES NULL Último clic

Índices

  • PRIMARY KEY (id)
  • UNIQUE KEY (codigo)
  • INDEX (proyecto)
  • INDEX (estado)
  • INDEX (fecha_creacion)

🔐 Buenas Prácticas

1. Nunca usar synchronize: true en producción

// ❌ NUNCA
synchronize: true  // Puede perder datos

// ✅ SIEMPRE
synchronize: false  // Usar migraciones

2. Usar transacciones para operaciones críticas

await appDataSource.transaction(async (manager) => {
    await manager.save(link1);
    await manager.save(link2);
    // Si algo falla, todo se revierte
});

3. Validar antes de insertar

// Validar unicidad antes de crear
const exists = await shortLinkRepository.existsByCodigo(codigo);
if (exists) {
    throw new Error('Código ya existe');
}

4. Usar query builder para consultas complejas

const links = await shortLinkRepository
    .getRepository()
    .createQueryBuilder('link')
    .where('link.proyecto = :proyecto', { proyecto: 'Marketing' })
    .andWhere('link.totalClicks > :minClicks', { minClicks: 100 })
    .orderBy('link.totalClicks', 'DESC')
    .take(10)
    .getMany();

5. Índices para columnas frecuentemente buscadas

// Ya configurados en migración:
// - codigo (búsqueda por código)
// - proyecto (filtrado por proyecto)
// - estado (filtrado por estado)
// - fecha_creacion (ordenamiento)

📚 Referencias


Última actualización: Noviembre 2025
Autor: SimpleData Corp - Dey Gordillo