API de Gestión Financiera
API de Gestión Financiera - Sistema Empresarial
Sección titulada «API de Gestión Financiera - Sistema Empresarial»API de Gestión Financiera es una API REST completa y lista para producción diseñada para control financiero empresarial, demostrando características avanzadas de Veloce-TS con lógica de negocio del mundo real. Este ejemplo muestra cómo construir un sistema financiero robusto con autenticación, caché, trazabilidad de auditoría y gestión automática de inventario.
:::tip Listo para Producción Este ejemplo está diseñado para ser listo para producción e incluye todas las características que necesitarías en una aplicación empresarial real: caché, logging, monitoreo y manejo integral de errores. :::
🚀 Características Demostradas
Sección titulada «🚀 Características Demostradas»✅ Características del Framework
Sección titulada «✅ Características del Framework»- 7 Controladores REST (Auth, Ingresos, Gastos, Materiales, Empleados, Cheques, Notificaciones)
- Routing basado en decoradores con
@Controller,@Get,@Post, etc. - Validación automática con esquemas Zod
- Documentación OpenAPI/Swagger auto-generada
- Serialización de Request/Response (manejo de BigInt)
- Manejo de errores y estrategias de recuperación
✅ Autenticación y Seguridad
Sección titulada «✅ Autenticación y Seguridad»- Autenticación JWT con expiración configurable
- Hash de contraseñas usando bcrypt
- Rutas protegidas con middleware de autenticación manual
- Control basado en roles (extensible)
- Validación de tokens y manejo de errores
✅ Base de Datos y ORM
Sección titulada «✅ Base de Datos y ORM»- PostgreSQL como base de datos
- Prisma ORM para acceso type-safe a la base de datos
- Relaciones complejas entre entidades
- Agregaciones y consultas estadísticas
- Soporte de transacciones para integridad de datos
✅ Rendimiento y Caché
Sección titulada «✅ Rendimiento y Caché»- Caché Redis con configuración de TTL
- Invalidación inteligente de caché en cambios de datos
- Patrón Wrap para implementación limpia de caché
- Endpoint de monitoreo de caché para debugging
✅ Lógica de Negocio Avanzada
Sección titulada «✅ Lógica de Negocio Avanzada»- Gestión auto-inventario (gastos crean materiales automáticamente)
- Resúmenes financieros con agregaciones
- Seguimiento de datos históricos
- Registro de auditoría para todas las operaciones
- Alertas de stock bajo para materiales
✅ Características de Producción
Sección titulada «✅ Características de Producción»- Logging estructurado con Winston
- Health checks (base de datos, Redis, memoria)
- Graceful shutdown (cierre elegante)
- Docker Compose para desarrollo fácil
- Configuración por variables de entorno
- Endpoints de monitoreo para Redis y estado del sistema
🏗️ Visión General de Arquitectura
Sección titulada «🏗️ Visión General de Arquitectura»financial-api/├── src/│ ├── controllers/ # 7 controladores REST│ │ ├── auth.controller.ts # Autenticación y perfil│ │ ├── income.controller.ts # Gestión de ingresos│ │ ├── expense.controller.ts # Gastos + auto-creación de materiales│ │ ├── material.controller.ts # Gestión de inventario│ │ ├── employee.controller.ts # Registros de empleados│ │ ├── check.controller.ts # Gestión de cheques│ │ └── notification.controller.ts # Notificaciones│ ├── schemas/ # Esquemas de validación Zod│ ├── config/ # Configuración│ │ ├── prisma.ts # Cliente Prisma│ │ └── redis.ts # Cliente Redis│ ├── utils/ # Utilidades│ │ ├── cache.ts # Servicio de caché│ │ ├── logger.ts # Logger Winston│ │ └── serializer.ts # Serialización de datos│ ├── middleware/ # Middleware personalizado│ │ └── auth.ts # Helper de autenticación│ └── index.ts # Punto de entrada├── prisma/│ └── schema.prisma # Esquema de base de datos├── scripts/│ └── create-admin-user.ts # Script de configuración admin├── docker-compose.yml # PostgreSQL + Redis└── public/ └── docs.html # Swagger UI personalizado🎯 Casos de Uso del Mundo Real
Sección titulada «🎯 Casos de Uso del Mundo Real»Este ejemplo es perfecto para:
- Sistemas de gestión financiera
- Aplicaciones de control de inventario
- Plataformas de gestión empresarial
- Software de contabilidad
- Sistemas ERP
🛠️ Inicio Rápido
Sección titulada «🛠️ Inicio Rápido»Prerrequisitos
Sección titulada «Prerrequisitos»- Node.js >= 18.0.0 o Bun >= 1.0.0
- Docker y Docker Compose
Instalación
Sección titulada «Instalación»- Clonar el repositorio:
git clone https://github.com/veloce-ts/examplescd examples/financial-management-api- Instalar dependencias:
npm install# obun install- Configurar entorno:
cp .env.example .envEditar .env:
# Base de datosDATABASE_URL="postgresql://garcia_user:garcia_password_2024@localhost:5432/garcia_renovacion"
# RedisREDIS_HOST="localhost"REDIS_PORT=6379REDIS_PASSWORD="garcia_redis_2024"
# JWTJWT_SECRET="tu-clave-super-secreta-cambiar-en-produccion"JWT_EXPIRES_IN="24h"
# CORSCORS_ORIGIN="http://localhost:5173"- Iniciar servicios (PostgreSQL + Redis):
docker-compose up -d- Ejecutar migraciones:
npx prisma migrate devnpx prisma generate- Crear usuario admin:
npm run create-admin- Iniciar servidor de desarrollo:
npm run dev# obun run devPuntos de Acceso
Sección titulada «Puntos de Acceso»- API Base: http://localhost:3000
- Swagger UI: http://localhost:3000/api/docs
- Especificación OpenAPI: http://localhost:3000/openapi.json
- Health Check: http://localhost:3000/api/health
- Monitor Redis: http://localhost:3000/api/debug/redis
📚 Ejemplos de Implementación Clave
Sección titulada «📚 Ejemplos de Implementación Clave»1. Autenticación JWT
Sección titulada «1. Autenticación JWT»@Controller('/api/auth')export class AuthController {
@Post('/login') async login(@Body(LoginSchema) body: LoginRequest) { // Buscar usuario const user = await prisma.user.findUnique({ where: { username: body.username } });
if (!user || !user.isActive) { throw new Error('Credenciales inválidas'); }
// Verificar contraseña const validPassword = await bcrypt.compare( body.password, user.passwordHash );
if (!validPassword) { throw new Error('Credenciales inválidas'); }
// Generar JWT const token = jwt.sign( { id: user.id, username: user.username, role: user.role }, process.env.JWT_SECRET!, { expiresIn: process.env.JWT_EXPIRES_IN || '24h' } );
return { success: true, data: { user, token } }; }}2. Validación con Zod
Sección titulada «2. Validación con Zod»export const CreateExpenseSchema = z.object({ concept: z.string().min(1, 'El concepto es requerido'), amount: z.number().positive('El monto debe ser positivo'), category: z.string().min(1, 'La categoría es requerida'), paymentMethod: z.string().min(1, 'El método de pago es requerido'), transactionDate: z.string() .refine(val => !isNaN(Date.parse(val)), 'Fecha inválida') .transform(val => new Date(val)),
// Campos de compra de material isMaterialPurchase: z.boolean().default(false), materialName: z.string().optional(), materialQuantity: z.number().positive().optional(), materialUnit: z.string().optional()}).refine((data) => { // Validación condicional if (data.isMaterialPurchase) { return data.materialName && data.materialQuantity && data.materialUnit; } return true;}, { message: 'Los campos de material son requeridos cuando isMaterialPurchase es true', path: ['materialName']});3. Estrategia de Caché Redis
Sección titulada «3. Estrategia de Caché Redis»@Get('/stats')async getMaterialStats(@Header('authorization') authHeader?: string) { const user = await authenticateUser(authHeader);
const cacheKey = cacheService.generateKey('material', 'stats');
const stats = await cacheService.wrap( cacheKey, async () => { // Operación costosa de base de datos const [total, active, inactive, aggregates, materials] = await Promise.all([ prisma.material.count(), prisma.material.count({ where: { status: 'activo' } }), prisma.material.count({ where: { status: 'inactivo' } }), prisma.material.aggregate({ _sum: { currentQuantity: true, unitCost: true }, where: { status: 'activo' } }), prisma.material.findMany({ where: { status: 'activo' }, select: { currentQuantity: true, unitCost: true } }) ]);
// Calcular valor total del inventario const totalValue = materials.reduce((sum, m) => { return sum + (Number(m.currentQuantity) * Number(m.unitCost || 0)); }, 0);
return { total, active, inactive, totalQuantity: Number(aggregates._sum.currentQuantity) || 0, totalValue, lowStock: await prisma.material.count({ where: { status: 'activo', currentQuantity: { lt: 10 } } }) }; }, 120 // Cachear por 2 minutos );
return { success: true, data: stats };}4. Auto-Crear Materiales desde Gastos
Sección titulada «4. Auto-Crear Materiales desde Gastos»Esto demuestra lógica de negocio compleja:
@Post('/')async createExpense( @Body(CreateExpenseSchema) body: CreateExpenseRequest, @Header('authorization') authHeader?: string) { const user = await authenticateUser(authHeader); let autoCreatedMaterialId: number | undefined;
// Auto-crear o actualizar material si es compra de material if (body.isMaterialPurchase && body.materialName && body.materialQuantity) { // Verificar si el material existe let material = await prisma.material.findFirst({ where: { name: body.materialName } });
if (material) { // Actualizar cantidad de material existente const updated = await prisma.material.update({ where: { id: material.id }, data: { currentQuantity: Number(material.currentQuantity) + Number(body.materialQuantity), unitCost: body.amount // Actualizar precio } }); autoCreatedMaterialId = updated.id;
logger.info('Material actualizado desde gasto', { materialId: updated.id, cantidadAgregada: body.materialQuantity }); } else { // Crear nuevo material const newMaterial = await prisma.material.create({ data: { code: body.materialCode || `MAT-${Date.now()}`, name: body.materialName, currentQuantity: body.materialQuantity, unitCost: body.amount, unit: body.materialUnit || 'unidad', minQuantity: 0, status: 'activo', userId: user.id } }); autoCreatedMaterialId = newMaterial.id;
logger.info('Material auto-creado desde gasto', { materialId: newMaterial.id, nombre: newMaterial.name }); } }
// Crear gasto con referencia al material const expense = await prisma.expense.create({ data: { ...body, userId: user.id, autoCreatedMaterialId } });
// Registro de auditoría await prisma.auditLog.create({ data: { tableName: 'expenses', recordId: expense.id, action: 'CREATE', newData: expense, userId: user.id } });
return { success: true, data: serializeData(expense) };}🎯 Endpoints de la API
Sección titulada «🎯 Endpoints de la API»Autenticación
Sección titulada «Autenticación»POST /api/auth/login- Login de usuarioPOST /api/auth/logout- Logout de usuarioGET /api/auth/profile- Obtener usuario actualPUT /api/auth/profile- Actualizar perfilPOST /api/auth/change-password- Cambiar contraseña
Gestión de Ingresos
Sección titulada «Gestión de Ingresos»GET /api/income- Listar ingresos (paginado)GET /api/income/summary- Resumen financiero (total, count, promedio)GET /api/income/history?months=6- Datos históricosPOST /api/income- Crear ingresoPUT /api/income/:id- Actualizar ingresoDELETE /api/income/:id- Eliminar ingreso
Gestión de Gastos
Sección titulada «Gestión de Gastos»GET /api/expenses- Listar gastos (paginado)GET /api/expenses/summary- Resumen de gastos con categorías principalesPOST /api/expenses- Crear gasto (auto-crea material si es necesario)PUT /api/expenses/:id- Actualizar gastoDELETE /api/expenses/:id- Eliminar gasto
Gestión de Materiales/Inventario
Sección titulada «Gestión de Materiales/Inventario»GET /api/materials- Listar materiales (con filtros)GET /api/materials/stats- Estadísticas de inventario (cacheado)GET /api/materials/low-stock- Materiales bajo cantidad mínimaPOST /api/materials- Crear materialPUT /api/materials/:id- Actualizar materialDELETE /api/materials/:id- Eliminar material
Sistema y Monitoreo
Sección titulada «Sistema y Monitoreo»GET /api/health- Health check (base de datos, Redis, memoria)GET /api/debug/redis- Monitoreo de RedisGET /docs- Documentación Veloce-TS generadaGET /api/docs- Swagger UI personalizadoGET /openapi.json- Especificación OpenAPI
🧪 Probar la API
Sección titulada «🧪 Probar la API»Usando cURL
Sección titulada «Usando cURL»# 1. LoginTOKEN=$(curl -s -X POST http://localhost:3000/api/auth/login \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"admin123"}' \ | jq -r '.data.token')
# 2. Obtener resumen de ingresoscurl -H "Authorization: Bearer $TOKEN" \ http://localhost:3000/api/income/summary
# 3. Crear gasto con creación automática de materialcurl -X POST http://localhost:3000/api/expenses \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "amount": 5000, "concept": "Compra de Cemento Portland", "category": "materiales", "supplier": "Ferretería ACME", "paymentMethod": "efectivo", "transactionDate": "2024-11-01", "isMaterialPurchase": true, "materialName": "Cemento Portland", "materialQuantity": 50, "materialUnit": "bolsas", "materialCode": "MAT-CEM-001" }'
# 4. Verificar que el material fue creadocurl -H "Authorization: Bearer $TOKEN" \ "http://localhost:3000/api/materials?searchTerm=Portland"Usando Swagger UI
Sección titulada «Usando Swagger UI»- Abrir http://localhost:3000/api/docs
- Click en botón “Authorize”
- Login a través del endpoint
/api/auth/login - Copiar el token de la respuesta
- Pegarlo como:
Bearer TU_TOKEN_AQUI - ¡Ahora puedes probar todos los endpoints interactivamente!
🔍 Monitoreo y Observabilidad
Sección titulada «🔍 Monitoreo y Observabilidad»Monitoreo de Redis
Sección titulada «Monitoreo de Redis»Visita http://localhost:3000/api/debug/redis para ver:
- Total de claves en caché
- Todas las claves de caché
- Estadísticas de conexión
- Datos de muestra del caché
- Patrones de caché utilizados
Logs de Aplicación
Sección titulada «Logs de Aplicación»Los logs se almacenan en el directorio logs/:
combined.log- Todos los logs de aplicaciónerror.log- Solo erroresexceptions.log- Excepciones no capturadasrejections.log- Rechazos de promesas no manejados
🚢 Despliegue en Producción
Sección titulada «🚢 Despliegue en Producción»Variables de Entorno
Sección titulada «Variables de Entorno»# Configuración de ProducciónNODE_ENV=productionPORT=3000
# Base de datos (usar credenciales de producción)DATABASE_URL=postgresql://user:pass@prod-db:5432/dbname
# Redis (usar credenciales de producción)REDIS_HOST=prod-redisREDIS_PORT=6379REDIS_PASSWORD=tu-contraseña-redis-segura
# JWT (¡CAMBIAR ESTOS!)JWT_SECRET=tu-clave-super-secreta-de-produccionJWT_EXPIRES_IN=24h
# CORSCORS_ORIGIN=https://tu-dominio-produccion.comDocker Deployment
Sección titulada «Docker Deployment»FROM oven/bun:1 as baseWORKDIR /app
# DependenciasCOPY package*.json bun.lockb ./RUN bun install --frozen-lockfile --production
# Código fuenteCOPY . .
# PrismaRUN bunx prisma generate
# BuildRUN bun run build
# Imagen de producciónFROM oven/bun:1-slimWORKDIR /app
COPY --from=base /app/dist ./distCOPY --from=base /app/node_modules ./node_modulesCOPY --from=base /app/prisma ./prismaCOPY --from=base /app/package.json ./
EXPOSE 3000
# Health checkHEALTHCHECK --interval=30s --timeout=3s --start-period=10s \ CMD curl -f http://localhost:3000/api/health || exit 1
CMD ["bun", "run", "start"]📈 Consideraciones de Rendimiento
Sección titulada «📈 Consideraciones de Rendimiento»Estrategia de Caché
Sección titulada «Estrategia de Caché»- Estadísticas de materiales: TTL de 2 minutos
- Invalidación de caché en crear/actualizar/eliminar
- Invalidación basada en patrones para datos relacionados
Optimización de Base de Datos
Sección titulada «Optimización de Base de Datos»- Índices en campos consultados frecuentemente
- Recuperación selectiva de campos con
select - Connection pooling configurado
- Consultas de agregación optimizadas
🎓 Resultados de Aprendizaje
Sección titulada «🎓 Resultados de Aprendizaje»Después de estudiar este ejemplo, comprenderás:
- ✅ Construir APIs listas para producción con Veloce-TS
- ✅ Implementar autenticación JWT
- ✅ Diseño de base de datos y uso de Prisma ORM
- ✅ Estrategias de caché con Redis
- ✅ Implementación de lógica de negocio compleja
- ✅ Logging estructurado y monitoreo
- ✅ Manejo y recuperación de errores
- ✅ Documentación de API con OpenAPI
- ✅ Containerización con Docker
- ✅ Estrategias de despliegue en producción
🔗 Recursos Adicionales
Sección titulada «🔗 Recursos Adicionales»- Documentación de Veloce-TS
- Guía de Autenticación
- Guía de Caché
- Documentación de Prisma
- Mejores Prácticas de Redis
🤝 Contribuir
Sección titulada «🤝 Contribuir»¿Encontraste un bug o quieres mejorar este ejemplo?
📝 Licencia
Sección titulada «📝 Licencia»Licencia MIT - ¡Libre para usar y modificar en tus proyectos!
:::tip ¿Listo para Construir? Este ejemplo proporciona una base sólida para construir sistemas financieros empresariales. ¡Haz un fork, personalízalo y hazlo tuyo! :::