expressed-example-app 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # Express-Router-BDS Example Application
2
+
3
+ This is a sample Express.js application demonstrating the usage of the `express-router-bds` routing system with custom hooks and advanced features.
4
+
5
+ ## Overview
6
+
7
+ This example application showcases:
8
+
9
+ - High-performance routing with `express-router-bDS`
10
+ - JSON-based route configuration
11
+ - Custom hook system implementation
12
+ - Parameterized route handling
13
+ - Directory-based route loading
14
+
15
+ ## Features
16
+
17
+ - **Modular Route Configuration**: Routes defined in JSON files for easy maintenance
18
+ - **Custom Hooks**: Comprehensive hook system with `pre_route_load`, `post_route_load`, `before_route_handler`, `after_route_handler`, `route_handler_error`, and more
19
+ - **Parameterized Routes**: Support for dynamic routes like `/users/:id` and `/products/:category/:id`
20
+ - **Extensible Architecture**: Easy to extend with custom middleware and hooks
21
+
22
+ ## Project Structure
23
+
24
+ ```
25
+ express-app/
26
+ ├── app.js # Main application entry point
27
+ ├── hooks-config.js # Base hook configurations
28
+ ├── hooks-personalizados.js # Custom hook implementations
29
+ ├── controllers/
30
+ │ ├── mainController.js # Home and init route handlers
31
+ │ ├── productController.js # Product route handlers
32
+ │ └── userController.js # User route handlers
33
+ ├── routes/
34
+ │ └── main-routes.json # Route definitions in JSON format
35
+ └── node_modules/
36
+ └── express-router-bds/ # Custom routing system
37
+ ```
38
+
39
+ ## Available Endpoints
40
+
41
+ - `GET /` - Home page with welcome message
42
+ - `GET /init` - Initialization endpoint
43
+ - `GET /users/:id` - Get user by ID
44
+ - `GET /products/:category/:id` - Get product by category and ID
45
+ - `GET /test-hook` - Test endpoint for hook demonstration
46
+
47
+ ## Hook System
48
+
49
+ The application demonstrates several types of hooks:
50
+
51
+ - `pre_route_load` - Executes before loading routes from a file
52
+ - `post_route_load` - Executes after loading routes from a file
53
+ - `before_route_processing` - Executes before processing any route
54
+ - `route_matched` - Executes when a matching route is found
55
+ - `before_route_handler` - Executes before executing a route handler
56
+ - `after_route_handler` - Executes after executing a route handler
57
+ - `route_handler_error` - Executes when a route handler error occurs
58
+
59
+ ## Installation
60
+
61
+ ```bash
62
+ npm install
63
+ ```
64
+
65
+ ## Usage
66
+
67
+ Start the server:
68
+
69
+ ```bash
70
+ npm start
71
+ ```
72
+
73
+ The application will be available at `http://localhost:3000`
74
+
75
+ ## Testing
76
+
77
+ Test the endpoints:
78
+
79
+ ```bash
80
+ curl http://localhost:3000
81
+ curl http://localhost:3000/users/123
82
+ curl http://localhost:3000/products/electronics/456
83
+ ```
84
+
85
+ ## License
86
+
87
+ This project is part of the `express-router-bds` ecosystem and follows the same licensing terms.
package/app.js CHANGED
@@ -1,115 +1,62 @@
1
- /**
2
- * Aplicación principal
3
- * Integración de @hooked, @router y @queued con una arquitectura limpia
4
- */
5
-
6
1
  const express = require('express');
7
- const fs = require('fs');
8
- const path = require('path');
9
- const ExpressHooked = require('express-hooked');
10
2
  const ExpressRouterIntegration = require('express-router-bds');
11
- const ExpressQueueIntegration = require('express-queued');
12
- const AppHooks = require('./hooks/AppHooks');
13
- const { captureBodyMiddleware } = require('./middleware/bodyCapture');
3
+ const RouteDirectoryLoader = require('express-router-bds/RouteDirectoryLoader');
4
+ const AppHooks = require('./hooks-config');
5
+ const HooksPersonalizados = require('./hooks-personalizados');
14
6
 
15
- // Crear la aplicación Express
16
7
  const app = express();
17
- const port = 3001;
18
-
19
- // Leer la configuración de rutas desde el archivo JSON
20
- const routesConfig = JSON.parse(fs.readFileSync(path.join(__dirname, 'config/routes.json'), 'utf8'));
8
+ const PORT = process.env.PORT || 3000;
21
9
 
22
- // Inicializar el sistema de hooks
23
- const hooked = new ExpressHooked(app);
10
+ // Crear instancia del router
11
+ const routerIntegration = new ExpressRouterIntegration();
24
12
 
25
- // Inicializar el sistema de enrutamiento
26
- const router = new ExpressRouterIntegration();
13
+ // Inicializar hooks y filtros
14
+ const appHooks = new AppHooks(app, routerIntegration.getHooks());
27
15
 
28
- // Inicializar el sistema de colas
29
- const queueSystem = new ExpressQueueIntegration();
16
+ // Inicializar hooks personalizados
17
+ const hooksPersonalizados = new HooksPersonalizados(routerIntegration);
30
18
 
31
- // Registrar los hooks de la aplicación
32
- AppHooks.registerHooks(hooked);
33
19
 
34
- // Middleware para capturar el body
35
- app.use(captureBodyMiddleware);
20
+ // Cargar rutas desde directorio
21
+ const routeDirectoryLoader = new RouteDirectoryLoader();
36
22
 
37
- // Middleware para servir archivos estáticos
38
- app.use(express.static(path.join(__dirname, 'public')));
39
23
 
40
- // Cargar dinámicamente los controladores y registrar las rutas
41
- for (const route of routesConfig.routes) {
42
- // Extraer el nombre del controlador y la acción
43
- const [controllerName, actionName] = route.controller.split('.');
44
-
45
- // Construir la ruta del archivo del controlador
46
- const controllerPath = path.join(__dirname, 'controllers', `${controllerName}.js`);
47
-
48
- // Verificar si el archivo del controlador existe
49
- if (fs.existsSync(controllerPath)) {
50
- // Importar el controlador
51
- const Controller = require(controllerPath);
52
-
53
- // Verificar si la acción existe en el controlador
54
- if (Controller[actionName]) {
55
- // Registrar la ruta con el método HTTP, la ruta y el controlador
56
- router.addRoute(route.method, route.path, (req, res) => {
57
- // Disparar hook antes de ejecutar la acción
58
- hooked.doAction('before_controller_action', req, res, route);
59
-
60
- // Disparar hook para mostrar información detallada de la solicitud
61
- // Usar el body capturado en lugar del body original que puede estar vacío
62
- if (req.capturedBody) {
63
- req.body = req.capturedBody;
64
- }
65
- hooked.doAction('request_details_log', req, res, route);
66
-
67
- // Ejecutar la acción del controlador
68
- Controller[actionName](req, res);
69
-
70
- // Disparar hook después de ejecutar la acción
71
- hooked.doAction('after_controller_action', req, res, route);
72
- });
73
- } else {
74
- console.error(`La acción "${actionName}" no existe en el controlador "${controllerName}"`);
75
- }
76
- } else {
77
- console.error(`El archivo del controlador "${controllerPath}" no existe`);
24
+ routerIntegration.addHook('route_matched', (matchedRoute, req, res) => {
25
+ console.log(`Ruta encontrada: ${matchedRoute.route.path}`);
26
+ if (matchedRoute.params && Object.keys(matchedRoute.params).length > 0) {
27
+ console.log(`Parámetros:`, matchedRoute.params);
78
28
  }
79
- }
80
-
81
- // Aplicar el middleware de colas para inyectar el sistema en las solicitudes
82
- app.use(queueSystem.queueMiddleware());
83
-
84
- // Aplicar el router a la aplicación
85
- router.applyToApp(app);
29
+ });
86
30
 
87
- // Iniciar el sistema de colas
88
- queueSystem.start();
31
+ // Cargar rutas desde el directorio
32
+ routeDirectoryLoader.loadRoutesFromDirectory(routerIntegration, './routes')
33
+ .then(routes => {
34
+ console.log(`${routes.length} rutas cargadas desde directorio`);
35
+ // Mostrar las rutas que se han cargado
36
+ routes.forEach(route => {
37
+ console.log(`Ruta cargada: ${route.method} ${route.path}`);
38
+ });
39
+ })
40
+ .catch(error => {
41
+ console.error('Error cargando rutas desde directorio:', error.message);
42
+ });
89
43
 
90
- // Iniciar el sistema de hooks
91
- hooked.init();
44
+ // Aplicar el router a la aplicación Express
45
+ routerIntegration.applyToApp(app);
92
46
 
93
- // Middleware para manejo de errores
94
- app.use((err, req, res, next) => {
95
- console.error('Error:', err);
96
- res.status(500).json({
97
- error: 'Ocurrió un error en el servidor',
98
- message: err.message
99
- });
47
+ // Middleware para manejar errores 404
48
+ app.use((req, res) => {
49
+ res.status(404).json({ error: 'Ruta no encontrada' });
100
50
  });
101
51
 
102
52
  // Iniciar el servidor
103
- app.listen(port, () => {
104
- console.log(`Servidor corriendo en http://localhost:${port}`);
105
- console.log('Módulos integrados:');
106
- console.log('- @hooked: Sistema de hooks activo');
107
- console.log('- @router: Sistema de enrutamiento activo');
108
- console.log('- @queued: Sistema de colas activo');
109
-
110
- // Mostrar rutas registradas
111
- console.log('\nRutas disponibles:');
112
- routesConfig.routes.forEach(route => {
113
- console.log(`${route.method} ${route.path} -> ${route.controller}.${route.action}`);
114
- });
115
- });
53
+ app.listen(PORT, () => {
54
+ console.log(`Servidor corriendo en el puerto ${PORT}`);
55
+ console.log('Endpoints disponibles:');
56
+ console.log('- GET /');
57
+ console.log('- GET /init');
58
+ console.log('- GET /users/:id');
59
+ console.log('- GET /products/:category/:id');
60
+ });
61
+
62
+ module.exports = app;
@@ -0,0 +1,177 @@
1
+ # Changelog Detallado: Modificaciones al Core de Express-Router-BDS
2
+
3
+ ## Archivo: `/node_modules/express-router-bds/ExpressRouter.js`
4
+
5
+ ### Cambio 1: Llamada al hook `before_route_processing`
6
+ - **Fecha**: 15 de febrero de 2026
7
+ - **Línea(s)**: 154
8
+ - **Antes**:
9
+ ```javascript
10
+ this.hooks.doAction('before_route_processing', req, res, next);
11
+ ```
12
+ - **Después**:
13
+ ```javascript
14
+ this.hooks.doAction('before_route_processing', req, res);
15
+ ```
16
+ - **Motivo**: Evitar que el hook reciba el parámetro `next` para prevenir conflictos con el flujo normal de Express
17
+
18
+ ### Cambio 2: Manejo de headers en ejecución del handler
19
+ - **Fecha**: 15 de febrero de 2026
20
+ - **Líneas**: 185-187
21
+ - **Antes**:
22
+ ```javascript
23
+ if (!res.headersSent) {
24
+ matchedRoute.route.handler(req, res, next);
25
+ } else {
26
+ console.warn('Headers ya fueron enviados, no se puede ejecutar el handler');
27
+ }
28
+ ```
29
+ - **Después**:
30
+ ```javascript
31
+ if (!res.headersSent) {
32
+ // Añadir logging para depuración
33
+ console.log(`Ejecutando handler para ruta: ${req.method} ${req.url}`);
34
+ matchedRoute.route.handler(req, res, next);
35
+ } else {
36
+ console.warn('Headers ya fueron enviados, no se puede ejecutar el handler');
37
+ next();
38
+ }
39
+ ```
40
+ - **Motivo**: Añadir logging para depuración y asegurar que se llame a `next()` si los headers ya han sido enviados
41
+
42
+ ### Cambio 3: Llamada a hooks de handler
43
+ - **Fecha**: 15 de febrero de 2026
44
+ - **Línea(s)**: 180
45
+ - **Antes**:
46
+ ```javascript
47
+ this.hooks.doAction('before_route_handler', req, res, next);
48
+ ```
49
+ - **Después**:
50
+ ```javascript
51
+ this.hooks.doAction('before_route_handler', req, res);
52
+ ```
53
+ - **Motivo**: Consistencia con el manejo de hooks para evitar conflictos con `next()`
54
+
55
+ ### Cambio 4: Manejo de errores en handler
56
+ - **Fecha**: 15 de febrero de 2026
57
+ - **Línea(s)**: 193
58
+ - **Antes**:
59
+ ```javascript
60
+ this.hooks.doAction('route_handler_error', error, req, res, next);
61
+ ```
62
+ - **Después**:
63
+ ```javascript
64
+ this.hooks.doAction('route_handler_error', error, req, res);
65
+ ```
66
+ - **Motivo**: Consistencia con el manejo de hooks para evitar conflictos con `next()`
67
+
68
+ ## Archivo: `/node_modules/express-router-bds/RouteLoader.js`
69
+
70
+ ### Cambio 1: Manejo seguro de headers en handlers con contentType
71
+ - **Fecha**: 15 de febrero de 2026
72
+ - **Líneas**: 178-195
73
+ - **Antes**:
74
+ ```javascript
75
+ // Ejecutar el handler original
76
+ try {
77
+ if (handler.constructor.name === 'AsyncFunction') {
78
+ await handler(req, res, next);
79
+ } else {
80
+ handler(req, res, next);
81
+ }
82
+ } catch (error) {
83
+ // Si ocurre un error, asegurarse de no intentar enviar headers otra vez
84
+ if (!res.headersSent) {
85
+ next(error);
86
+ } else {
87
+ console.error('Error en el handler:', error);
88
+ }
89
+ }
90
+ ```
91
+ - **Después**:
92
+ ```javascript
93
+ // Ejecutar el handler original solo si no se han enviado headers aún
94
+ if (!res.headersSent) {
95
+ try {
96
+ if (handler.constructor.name === 'AsyncFunction') {
97
+ await handler(req, res, next);
98
+ } else {
99
+ handler(req, res, next);
100
+ }
101
+ } catch (error) {
102
+ // Si ocurre un error, asegurarse de no intentar enviar headers otra vez
103
+ if (!res.headersSent) {
104
+ next(error);
105
+ } else {
106
+ console.error('Error en el handler:', error);
107
+ }
108
+ }
109
+ }
110
+ ```
111
+ - **Motivo**: Asegurar que los handlers no se ejecuten si los headers ya han sido enviados
112
+
113
+ ### Cambio 2: Eliminación de wrapper adicional
114
+ - **Fecha**: 15 de febrero de 2026
115
+ - **Líneas**: 197-217
116
+ - **Antes**:
117
+ ```javascript
118
+ // Crear un handler que envuelva completamente la lógica para prevenir doble ejecución
119
+ const wrappedHandler = async (req, res, next) => {
120
+ // Verificar que no se hayan enviado headers aún
121
+ if (res.headersSent) {
122
+ return;
123
+ }
124
+
125
+ // Ejecutar el handler final
126
+ try {
127
+ await finalHandler(req, res, next);
128
+ } catch (error) {
129
+ if (!res.headersSent) {
130
+ next(error);
131
+ } else {
132
+ console.error('Error en el handler:', error);
133
+ }
134
+ }
135
+ };
136
+
137
+ // Agregar la ruta con el handler envuelto
138
+ routerIntegration.addRoute(route.method, route.path, wrappedHandler);
139
+ ```
140
+ - **Después**:
141
+ ```javascript
142
+ // Agregar la ruta con el handler final (sin doble wrapper)
143
+ // Asegurarse de que el handler es correcto
144
+ routerIntegration.addRoute(route.method, route.path, finalHandler);
145
+ ```
146
+ - **Motivo**: Eliminar wrapper redundante que podría causar problemas de ejecución doble
147
+
148
+ ## Archivo: `/node_modules/express-router-bds/RouteMatcher.js`
149
+
150
+ ### Cambio 1: Mejora en la conversión de rutas a expresiones regulares
151
+ - **Fecha**: 15 de febrero de 2026
152
+ - **Líneas**: 213-225
153
+ - **Antes**:
154
+ ```javascript
155
+ // Lógica de escape de caracteres especiales
156
+ let escapedPath = '';
157
+ for (let i = 0; i < path.length; i++) {
158
+ const char = path[i];
159
+ if (char.match(/[.+?^${}()|[\]\\-]/) && !(i > 0 && path[i-1] === ':')) {
160
+ escapedPath += '\\' + char;
161
+ } else {
162
+ escapedPath += char;
163
+ }
164
+ }
165
+ ```
166
+ - **Después**: (Sin cambios, ya que no se modificó este archivo directamente)
167
+
168
+ ## Resumen de Impacto
169
+
170
+ Los cambios realizados al core de `express-router-bds` se enfocaron principalmente en:
171
+
172
+ 1. **Evitar conflictos con el parámetro `next`** en las llamadas a hooks
173
+ 2. **Mejorar la seguridad en el manejo de headers** para prevenir el error `ERR_HTTP_HEADERS_SENT`
174
+ 3. **Eliminar wrappers redundantes** que podían causar ejecuciones dobles de handlers
175
+ 4. **Agregar verificaciones adicionales** para asegurar que los handlers solo se ejecuten cuando sea seguro hacerlo
176
+
177
+ Estos cambios resolvieron el problema fundamental donde las rutas se identificaban pero los handlers no se ejecutaban, permitiendo que la aplicación funcione correctamente.
@@ -0,0 +1,34 @@
1
+ // controllers/mainController.js
2
+ // Controlador principal de la aplicación
3
+
4
+ class MainController {
5
+ /**
6
+ * Maneja la ruta raíz
7
+ * @param {Object} req - Objeto de solicitud
8
+ * @param {Object} res - Objeto de respuesta
9
+ */
10
+ home(req, res) {
11
+ if (!res.headersSent) {
12
+ res.json({
13
+ message: '¡Bienvenido a la aplicación Express con Router-BDS!',
14
+ timestamp: new Date().toISOString()
15
+ });
16
+ } else {
17
+ console.log('Headers ya fueron enviados en home handler');
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Maneja la ruta de inicialización
23
+ * @param {Object} req - Objeto de solicitud
24
+ * @param {Object} res - Objeto de respuesta
25
+ */
26
+ init(req, res) {
27
+ res.json({
28
+ message: 'Aplicación inicializada correctamente con Router-BDS',
29
+ status: 'success'
30
+ });
31
+ }
32
+ }
33
+
34
+ module.exports = new MainController();
@@ -0,0 +1,22 @@
1
+ // controllers/productController.js
2
+ // Controlador para gestionar productos
3
+
4
+ class ProductController {
5
+ /**
6
+ * Obtiene un producto por categoría e ID
7
+ * @param {Object} req - Objeto de solicitud
8
+ * @param {Object} res - Objeto de respuesta
9
+ */
10
+ getProduct(req, res) {
11
+ const category = req.params.category;
12
+ const productId = req.params.id;
13
+ res.json({
14
+ message: `Obteniendo producto de la categoría "${category}" con ID: ${productId}`,
15
+ category: category,
16
+ productId: productId,
17
+ timestamp: new Date().toISOString()
18
+ });
19
+ }
20
+ }
21
+
22
+ module.exports = new ProductController();
@@ -0,0 +1,20 @@
1
+ // controllers/userController.js
2
+ // Controlador para gestionar usuarios
3
+
4
+ class UserController {
5
+ /**
6
+ * Obtiene un usuario por ID
7
+ * @param {Object} req - Objeto de solicitud
8
+ * @param {Object} res - Objeto de respuesta
9
+ */
10
+ getUser(req, res) {
11
+ const userId = req.params.id;
12
+ res.json({
13
+ message: `Obteniendo información del usuario con ID: ${userId}`,
14
+ userId: userId,
15
+ timestamp: new Date().toISOString()
16
+ });
17
+ }
18
+ }
19
+
20
+ module.exports = new UserController();
@@ -0,0 +1,114 @@
1
+ # Documento de Análisis: Bug de Enrutamiento en Express-Router-BDS
2
+
3
+ ## Descripción del Problema
4
+
5
+ La aplicación Express con el sistema de enrutamiento personalizado `express-router-bds` presentaba un fallo crítico donde:
6
+
7
+ - Las rutas se identificaban correctamente en el sistema
8
+ - El log mostraba mensajes como "Ruta encontrada: /"
9
+ - Sin embargo, los handlers correspondientes no se ejecutaban
10
+ - El sistema respondía con "Ruta no encontrada" para todas las rutas válidas
11
+ - Aparecía el error `ERR_HTTP_HEADERS_SENT: Cannot set headers after they are sent to the client`
12
+
13
+ ## Análisis Técnico
14
+
15
+ ### Componentes Involucrados
16
+
17
+ 1. **ExpressRouter.js** - Middleware principal de enrutamiento
18
+ 2. **hooks-config.js** - Configuración de hooks personalizados
19
+ 3. **RouteMatcher.js** - Sistema de coincidencia de rutas
20
+ 4. **RouteLoader.js** - Cargador de rutas desde archivos JSON
21
+
22
+ ### Causa Raíz
23
+
24
+ El problema se originó por una **interacción incorrecta entre el sistema de hooks y el flujo normal de Express**:
25
+
26
+ 1. El hook `before_route_processing` se definía con 3 parámetros: `(req, res, next)`
27
+ 2. Este hook se registraba en `hooks-config.js` y se llamaba en `ExpressRouter.js`
28
+ 3. El hook contenía una llamada a `next()`, lo que causaba que `next()` se ejecutara dos veces:
29
+ - Una vez por el hook
30
+ - Otra vez por el flujo normal del middleware
31
+ 4. Esto provocaba que los headers se enviaran antes de tiempo
32
+ 5. Cuando el sistema intentaba ejecutar el handler real, fallaba porque los headers ya habían sido enviados
33
+
34
+ ### Flujo de Ejecución Defectuoso
35
+
36
+ ```javascript
37
+ // En ExpressRouter.js
38
+ this.hooks.doAction('before_route_processing', req, res, next);
39
+
40
+ // En hooks-config.js
41
+ this.hooks.addAction('before_route_processing', (req, res, next) => {
42
+ console.log(`[HOOK-ACTIVADO] Procesando ruta: ${req.method} ${req.url}`);
43
+ next(); // ← ESTO CAUSABA EL PROBLEMA
44
+ });
45
+ ```
46
+
47
+ ## Solución Implementada
48
+
49
+ ### Cambios Realizados
50
+
51
+ #### 1. En `ExpressRouter.js`
52
+
53
+ ```javascript
54
+ // ANTES
55
+ this.hooks.doAction('before_route_processing', req, res, next);
56
+
57
+ // DESPUÉS
58
+ this.hooks.doAction('before_route_processing', req, res);
59
+ ```
60
+
61
+ #### 2. En `hooks-config.js`
62
+
63
+ ```javascript
64
+ // ANTES
65
+ this.hooks.addAction('before_route_processing', (req, res, next) => {
66
+ console.log(`[HOOK-ACTIVADO] Procesando ruta: ${req.method} ${req.url}`);
67
+ next(); // ← ESTA LLAMADA CAUSABA EL CONFLICTO
68
+ });
69
+
70
+ // DESPUÉS
71
+ this.hooks.addAction('before_route_processing', (req, res, next) => {
72
+ console.log(`[HOOK-ACTIVADO] Procesando ruta: ${req.method} ${req.url}`);
73
+ // next(); ← COMENTADO PARA EVITAR DUPLICACIÓN
74
+ });
75
+ ```
76
+
77
+ #### 3. En `mainController.js` (como medida preventiva)
78
+
79
+ ```javascript
80
+ home(req, res) {
81
+ if (!res.headersSent) {
82
+ res.json({
83
+ message: '¡Bienvenido a la aplicación Express con Router-BDS!',
84
+ timestamp: new Date().toISOString()
85
+ });
86
+ } else {
87
+ console.log('Headers ya fueron enviados en home handler');
88
+ }
89
+ }
90
+ ```
91
+
92
+ ## Resultado
93
+
94
+ Después de aplicar la solución:
95
+
96
+ ✅ **Todas las rutas estáticas funcionan** (`/`, `/init`)
97
+ ✅ **Todas las rutas parametrizadas funcionan** (`/users/:id`, `/products/:category/:id`)
98
+ ✅ **No hay más errores de headers duplicados**
99
+ ✅ **Los parámetros de ruta se pasan correctamente**
100
+ ✅ **El sistema de hooks sigue funcionando para logging**
101
+
102
+ ## Lecciones Aprendidas
103
+
104
+ 1. **Separación de responsabilidades**: Los hooks deben usarse para extensibilidad, no para controlar el flujo principal
105
+ 2. **Cuidado con `next()`**: Llamar a `next()` en múltiples lugares puede causar efectos colaterales
106
+ 3. **Importancia de pruebas**: Este tipo de error puede pasar desapercibido sin pruebas adecuadas
107
+ 4. **Logging vs. Control de Flujo**: Los hooks de logging no deben alterar el flujo de la aplicación
108
+
109
+ ## Recomendaciones
110
+
111
+ - Implementar pruebas unitarias para el sistema de enrutamiento
112
+ - Documentar claramente la responsabilidad de cada hook
113
+ - Considerar el uso de un sistema de middleware más explícito para evitar confusiones
114
+ - Añadir validaciones para prevenir llamadas duplicadas a `next()`
@@ -0,0 +1,42 @@
1
+ // hooks-config.js
2
+ // Archivo de configuración para los hooks y filtros de la aplicación
3
+
4
+ class AppHooks {
5
+ constructor(expressApp, hookedInstance) {
6
+ this.app = expressApp;
7
+ this.hooks = hookedInstance;
8
+
9
+ this.initHooks();
10
+ this.initFilters();
11
+ }
12
+
13
+ initHooks() {
14
+ // Hook para antes de procesar cualquier ruta - SOLO PARA LOGGING
15
+ this.hooks.addAction('before_route_processing', (req, res, next) => {
16
+ console.log(`[HOOK-ACTIVADO] Procesando ruta: ${req.method} ${req.url}`);
17
+ // IMPORTANTE: No llamar a next() aquí, eso es responsabilidad del middleware
18
+ // next(); // Comentado para evitar la duplicación de next()
19
+ }, 10, 3); // Prioridad 10 y acepta 3 argumentos
20
+ }
21
+
22
+ initFilters() {
23
+ // Filtro para modificar el mensaje de bienvenida
24
+ this.hooks.addFilter('welcome_message', (message, req) => {
25
+ // Agregar información del usuario o fecha al mensaje
26
+ const timestamp = new Date().toLocaleTimeString();
27
+ return `${message} - Petición recibida a las ${timestamp}`;
28
+ }, 10, 2); // Prioridad 10 y acepta 2 argumentos
29
+ }
30
+
31
+ // Método para aplicar un filtro específico
32
+ applyWelcomeMessageFilter(message, req) {
33
+ return this.hooks.applyFilters('welcome_message', message, req);
34
+ }
35
+
36
+ // Método para ejecutar un hook específico
37
+ executeRootBeforeHook(req, res, next) {
38
+ this.hooks.doAction('root:before', req, res, next);
39
+ }
40
+ }
41
+
42
+ module.exports = AppHooks;
@@ -0,0 +1,54 @@
1
+ // hooks-personalizados.js
2
+ // Archivo de hooks personalizados para la aplicación
3
+
4
+ class HooksPersonalizados {
5
+ constructor(routerIntegration) {
6
+ this.routerIntegration = routerIntegration;
7
+ this.initHooks();
8
+ }
9
+
10
+ initHooks() {
11
+ // Hook que se ejecuta antes de cargar rutas desde un archivo
12
+ this.routerIntegration.addHook('pre_route_load', (filePath, routerIntegration) => {
13
+ console.log(`[PRE_ROUTE_LOAD] Iniciando carga de rutas desde: ${filePath}`);
14
+ }, 10, 2);
15
+
16
+ // Hook que se ejecuta después de cargar rutas desde un archivo
17
+ this.routerIntegration.addHook('post_route_load', (routes, routerIntegration) => {
18
+ console.log(`[POST_ROUTE_LOAD] Se cargaron ${routes.length} rutas exitosamente`);
19
+ routes.forEach((route, index) => {
20
+ console.log(`[POST_ROUTE_LOAD] Ruta ${index + 1}: ${route.method} ${route.path}`);
21
+ });
22
+ }, 10, 2);
23
+
24
+ // Hook que se ejecuta antes de procesar cualquier ruta
25
+ this.routerIntegration.addHook('before_route_processing', (req, res) => {
26
+ console.log(`[BEFORE_ROUTE_PROCESSING] Procesando ruta: ${req.method} ${req.url}`);
27
+ }, 10, 2);
28
+
29
+ // Hook que se ejecuta cuando se encuentra una ruta coincidente
30
+ this.routerIntegration.addHook('route_matched', (matchedRoute, req, res) => {
31
+ console.log(`[ROUTE_MATCHED] Ruta encontrada: ${matchedRoute.route.path}`);
32
+ if (matchedRoute.params && Object.keys(matchedRoute.params).length > 0) {
33
+ console.log(`[ROUTE_MATCHED] Parámetros:`, matchedRoute.params);
34
+ }
35
+ }, 10, 3);
36
+
37
+ // Hook que se ejecuta antes de ejecutar un handler de ruta
38
+ this.routerIntegration.addHook('before_route_handler', (req, res) => {
39
+ console.log(`[BEFORE_ROUTE_HANDLER] Antes de ejecutar handler para: ${req.method} ${req.url}`);
40
+ }, 10, 2);
41
+
42
+ // Hook que se ejecuta después de ejecutar un handler de ruta
43
+ this.routerIntegration.addHook('after_route_handler', (req, res) => {
44
+ console.log(`[AFTER_ROUTE_HANDLER] Después de ejecutar handler para: ${req.method} ${req.url}`);
45
+ }, 10, 2);
46
+
47
+ // Hook que se ejecuta cuando ocurre un error en un handler de ruta
48
+ this.routerIntegration.addHook('route_handler_error', (error, req, res) => {
49
+ console.log(`[ROUTE_HANDLER_ERROR] Error en handler para: ${req.method} ${req.url}`, error.message);
50
+ }, 10, 3);
51
+ }
52
+ }
53
+
54
+ module.exports = HooksPersonalizados;
package/package.json CHANGED
@@ -1,58 +1,19 @@
1
1
  {
2
2
  "name": "expressed-example-app",
3
- "version": "1.0.0",
4
- "description": "Example application integrating @hooked, @router and @queued",
3
+ "version": "2.0.0",
4
+ "description": "",
5
5
  "main": "app.js",
6
6
  "scripts": {
7
7
  "start": "node app.js",
8
- "dev": "nodemon app.js"
8
+ "test": "echo \"Error: no test specified\" && exit 1"
9
9
  },
10
+ "keywords": ["web express example"],
11
+ "author": "BDS",
12
+ "license": "ISC",
10
13
  "dependencies": {
11
14
  "express": "^4.22.1",
12
- "express-hooked": "^1.0.1",
15
+ "express-hooked": "^1.0.2",
13
16
  "express-queued": "^1.0.0",
14
- "express-router-bds": "^1.0.0"
15
- },
16
- "keywords": [
17
- "express",
18
- "hooks",
19
- "middleware",
20
- "router",
21
- "routing",
22
- "queues",
23
- "queue-system",
24
- "hook-system",
25
- "event-driven",
26
- "plugin-architecture",
27
- "modular",
28
- "scalable",
29
- "example",
30
- "demo",
31
- "boilerplate",
32
- "framework",
33
- "web-framework",
34
- "api",
35
- "rest-api",
36
- "microservices",
37
- "async",
38
- "asynchronous",
39
- "background-jobs",
40
- "task-queue",
41
- "express-plugins",
42
- "express-middleware",
43
- "nodejs",
44
- "javascript",
45
- "backend",
46
- "server"
47
- ],
48
- "author": "Benjamin Sanchez Cardenas",
49
- "license": "Apache-2.0",
50
- "repository": {
51
- "type": "git",
52
- "url": "https://gitlab.com/bytedogssyndicate1/expressed-example-app.git"
53
- },
54
- "bugs": {
55
- "url": "https://gitlab.com/bytedogssyndicate1/expressed-example-app/issues"
56
- },
57
- "homepage": "https://gitlab.com/bytedogssyndicate1/expressed-example-app#readme"
17
+ "express-router-bds": "^1.1.1"
18
+ }
58
19
  }
@@ -0,0 +1,37 @@
1
+ [
2
+ {
3
+ "method": "GET",
4
+ "path": "/",
5
+ "controller": "./controllers/mainController",
6
+ "handler": "home",
7
+ "contentType": "application/json"
8
+ },
9
+ {
10
+ "method": "GET",
11
+ "path": "/init",
12
+ "controller": "./controllers/mainController",
13
+ "handler": "init",
14
+ "contentType": "application/json"
15
+ },
16
+ {
17
+ "method": "GET",
18
+ "path": "/users/:id",
19
+ "controller": "./controllers/userController",
20
+ "handler": "getUser",
21
+ "contentType": "application/json"
22
+ },
23
+ {
24
+ "method": "GET",
25
+ "path": "/products/:category/:id",
26
+ "controller": "./controllers/productController",
27
+ "handler": "getProduct",
28
+ "contentType": "application/json"
29
+ },
30
+ {
31
+ "method": "GET",
32
+ "path": "/test-hook",
33
+ "controller": "./controllers/mainController",
34
+ "handler": "home",
35
+ "contentType": "application/json"
36
+ }
37
+ ]
package/test-hooks.js ADDED
@@ -0,0 +1,31 @@
1
+ // test-hooks.js
2
+ // Script para probar los hooks pre_route_load y post_route_load
3
+
4
+ const ExpressRouterIntegration = require('express-router-bds');
5
+ const RouteLoader = require('express-router-bds/RouteLoader');
6
+ const HooksPersonalizados = require('./hooks-personalizados');
7
+
8
+ async function testHooks() {
9
+ console.log('Iniciando prueba de hooks pre_route_load y post_route_load...');
10
+
11
+ // Crear instancia del router
12
+ const routerIntegration = new ExpressRouterIntegration();
13
+
14
+ // Inicializar hooks personalizados
15
+ const hooksPersonalizados = new HooksPersonalizados(routerIntegration);
16
+
17
+ // Crear instancia de RouteLoader
18
+ const routeLoader = new RouteLoader();
19
+
20
+ try {
21
+ // Cargar rutas desde el archivo de prueba
22
+ console.log('\nCargando rutas desde test-routes.json...\n');
23
+ const routes = await routeLoader.loadRoutes(routerIntegration, './test-routes.json');
24
+
25
+ console.log(`\nResultado: ${routes.length} rutas cargadas exitosamente`);
26
+ } catch (error) {
27
+ console.error('Error al cargar rutas:', error.message);
28
+ }
29
+ }
30
+
31
+ testHooks();
@@ -0,0 +1,9 @@
1
+ [
2
+ {
3
+ "method": "GET",
4
+ "path": "/test-hook",
5
+ "controller": "./controllers/mainController",
6
+ "handler": "home",
7
+ "contentType": "application/json"
8
+ }
9
+ ]
@@ -1,70 +0,0 @@
1
- {
2
- "routes": [
3
- {
4
- "method": "GET",
5
- "path": "/",
6
- "controller": "HomeController.index",
7
- "action": "index"
8
- },
9
- {
10
- "method": "GET",
11
- "path": "/users",
12
- "controller": "UserController.list",
13
- "action": "list"
14
- },
15
- {
16
- "method": "GET",
17
- "path": "/users/:id",
18
- "controller": "UserController.show",
19
- "action": "show"
20
- },
21
- {
22
- "method": "POST",
23
- "path": "/users",
24
- "controller": "UserController.create",
25
- "action": "create"
26
- },
27
- {
28
- "method": "PUT",
29
- "path": "/users/:id",
30
- "controller": "UserController.update",
31
- "action": "update"
32
- },
33
- {
34
- "method": "DELETE",
35
- "path": "/users/:id",
36
- "controller": "UserController.destroy",
37
- "action": "destroy"
38
- },
39
- {
40
- "method": "GET",
41
- "path": "/products",
42
- "controller": "ProductController.list",
43
- "action": "list"
44
- },
45
- {
46
- "method": "GET",
47
- "path": "/products/:id",
48
- "controller": "ProductController.show",
49
- "action": "show"
50
- },
51
- {
52
- "method": "POST",
53
- "path": "/products",
54
- "controller": "ProductController.create",
55
- "action": "create"
56
- },
57
- {
58
- "method": "GET",
59
- "path": "/queue/status",
60
- "controller": "QueueController.status",
61
- "action": "status"
62
- },
63
- {
64
- "method": "GET",
65
- "path": "/endpoints",
66
- "controller": "EndpointController.listEndpoints",
67
- "action": "listEndpoints"
68
- }
69
- ]
70
- }
@@ -1,43 +0,0 @@
1
- /**
2
- * Controlador de endpoints
3
- */
4
- class EndpointController {
5
- /**
6
- * Lista todos los endpoints disponibles
7
- * @param {Object} req - Objeto de solicitud
8
- * @param {Object} res - Objeto de respuesta
9
- */
10
- static listEndpoints(req, res) {
11
- // El sistema de hooks será utilizado para obtener la lista de endpoints
12
- if (req.app.locals.hooks) {
13
- // Simular la recopilación de endpoints desde el sistema de rutas
14
- const routes = [
15
- { method: 'GET', path: '/', description: 'Inicio de la aplicación' },
16
- { method: 'GET', path: '/users', description: 'Lista todos los usuarios' },
17
- { method: 'GET', path: '/users/:id', description: 'Muestra un usuario específico' },
18
- { method: 'POST', path: '/users', description: 'Crea un nuevo usuario' },
19
- { method: 'PUT', path: '/users/:id', description: 'Actualiza un usuario' },
20
- { method: 'DELETE', path: '/users/:id', description: 'Elimina un usuario' },
21
- { method: 'GET', path: '/products', description: 'Lista todos los productos' },
22
- { method: 'GET', path: '/products/:id', description: 'Muestra un producto específico' },
23
- { method: 'POST', path: '/products', description: 'Crea un nuevo producto' },
24
- { method: 'GET', path: '/queue/status', description: 'Estado del sistema de colas' },
25
- { method: 'GET', path: '/endpoints', description: 'Lista todos los endpoints disponibles' }
26
- ];
27
-
28
- // Permitir que los hooks modifiquen la lista de endpoints
29
- const processedEndpoints = req.app.locals.hooks.applyFilters('endpoints_list', routes);
30
-
31
- res.json({
32
- message: 'Lista de endpoints disponibles',
33
- endpoints: processedEndpoints
34
- });
35
- } else {
36
- res.status(500).json({
37
- error: 'Sistema de hooks no disponible'
38
- });
39
- }
40
- }
41
- }
42
-
43
- module.exports = EndpointController;
@@ -1,19 +0,0 @@
1
- /**
2
- * Controlador de inicio
3
- */
4
- class HomeController {
5
- /**
6
- * Acción de inicio
7
- * @param {Object} req - Objeto de solicitud
8
- * @param {Object} res - Objeto de respuesta
9
- */
10
- static index(req, res) {
11
- res.json({
12
- message: 'Bienvenido a la aplicación de ejemplo',
13
- version: '1.0.0',
14
- timestamp: new Date().toISOString()
15
- });
16
- }
17
- }
18
-
19
- module.exports = HomeController;
@@ -1,47 +0,0 @@
1
- /**
2
- * Controlador de productos
3
- */
4
- class ProductController {
5
- /**
6
- * Lista todos los productos
7
- * @param {Object} req - Objeto de solicitud
8
- * @param {Object} res - Objeto de respuesta
9
- */
10
- static list(req, res) {
11
- res.json({
12
- message: 'Lista de productos',
13
- products: [
14
- { id: 1, name: 'Laptop', price: 999.99 },
15
- { id: 2, name: 'Mouse', price: 29.99 }
16
- ]
17
- });
18
- }
19
-
20
- /**
21
- * Muestra un producto específico
22
- * @param {Object} req - Objeto de solicitud
23
- * @param {Object} res - Objeto de respuesta
24
- */
25
- static show(req, res) {
26
- const productId = req.params.id;
27
- res.json({
28
- message: `Información del producto ${productId}`,
29
- product: { id: productId, name: `Product ${productId}`, price: 19.99 }
30
- });
31
- }
32
-
33
- /**
34
- * Crea un nuevo producto
35
- * @param {Object} req - Objeto de solicitud
36
- * @param {Object} res - Objeto de respuesta
37
- */
38
- static create(req, res) {
39
- const productData = req.body;
40
- res.status(201).json({
41
- message: 'Producto creado exitosamente',
42
- product: { id: Date.now(), ...productData }
43
- });
44
- }
45
- }
46
-
47
- module.exports = ProductController;
@@ -1,26 +0,0 @@
1
- /**
2
- * Controlador del sistema de colas
3
- */
4
- class QueueController {
5
- /**
6
- * Obtiene el estado del sistema de colas
7
- * @param {Object} req - Objeto de solicitud
8
- * @param {Object} res - Objeto de respuesta
9
- */
10
- static status(req, res) {
11
- // El sistema de colas será inyectado por el middleware
12
- if (req.queueSystem) {
13
- const status = req.queueSystem.getStatus();
14
- res.json({
15
- message: 'Estado del sistema de colas',
16
- status: status
17
- });
18
- } else {
19
- res.status(500).json({
20
- error: 'Sistema de colas no disponible'
21
- });
22
- }
23
- }
24
- }
25
-
26
- module.exports = QueueController;
@@ -1,73 +0,0 @@
1
- /**
2
- * Controlador de usuarios
3
- */
4
- class UserController {
5
- /**
6
- * Lista todos los usuarios
7
- * @param {Object} req - Objeto de solicitud
8
- * @param {Object} res - Objeto de respuesta
9
- */
10
- static list(req, res) {
11
- res.json({
12
- message: 'Lista de usuarios',
13
- users: [
14
- { id: 1, name: 'John Doe', email: 'john@example.com' },
15
- { id: 2, name: 'Jane Smith', email: 'jane@example.com' }
16
- ]
17
- });
18
- }
19
-
20
- /**
21
- * Muestra un usuario específico
22
- * @param {Object} req - Objeto de solicitud
23
- * @param {Object} res - Objeto de respuesta
24
- */
25
- static show(req, res) {
26
- const userId = req.params.id;
27
- res.json({
28
- message: `Información del usuario ${userId}`,
29
- user: { id: userId, name: `User ${userId}`, email: `user${userId}@example.com` }
30
- });
31
- }
32
-
33
- /**
34
- * Crea un nuevo usuario
35
- * @param {Object} req - Objeto de solicitud
36
- * @param {Object} res - Objeto de respuesta
37
- */
38
- static create(req, res) {
39
- const userData = req.body;
40
- res.status(201).json({
41
- message: 'Usuario creado exitosamente',
42
- user: { id: Date.now(), ...userData }
43
- });
44
- }
45
-
46
- /**
47
- * Actualiza un usuario
48
- * @param {Object} req - Objeto de solicitud
49
- * @param {Object} res - Objeto de respuesta
50
- */
51
- static update(req, res) {
52
- const userId = req.params.id;
53
- const userData = req.body;
54
- res.json({
55
- message: `Usuario ${userId} actualizado`,
56
- user: { id: userId, ...userData }
57
- });
58
- }
59
-
60
- /**
61
- * Elimina un usuario
62
- * @param {Object} req - Objeto de solicitud
63
- * @param {Object} res - Objeto de respuesta
64
- */
65
- static destroy(req, res) {
66
- const userId = req.params.id;
67
- res.json({
68
- message: `Usuario ${userId} eliminado`
69
- });
70
- }
71
- }
72
-
73
- module.exports = UserController;
package/hooks/AppHooks.js DELETED
@@ -1,60 +0,0 @@
1
- /**
2
- * Archivo de hooks para la aplicación
3
- * Define todos los hooks utilizados en la aplicación
4
- */
5
-
6
- class AppHooks {
7
- /**
8
- * Registra todos los hooks de la aplicación
9
- * @param {ExpressHooked} hooked - Instancia del sistema de hooks
10
- */
11
- static registerHooks(hooked) {
12
- // Hook para registrar información cuando comienza una solicitud
13
- hooked.addAction('request_started', (req, res, next) => {
14
- console.log(`[${new Date().toISOString()}] Nueva solicitud: ${req.method} ${req.url}`);
15
- next();
16
- });
17
-
18
- // Hook para registrar información cuando se procesa una solicitud
19
- hooked.addAction('request_processed', (req, res) => {
20
- console.log(`[${new Date().toISOString()}] Solicitud procesada: ${req.method} ${req.url}`);
21
- });
22
-
23
- // Hook para registrar información detallada de cada solicitud
24
- hooked.addAction('request_details_log', (req, res, routeInfo) => {
25
- console.log(`\n--- Información de la Solicitud (mediante hook) ---`);
26
- console.log(`Fecha/Hora: ${new Date().toISOString()}`);
27
- console.log(`Método HTTP: ${req.method}`);
28
- console.log(`Ruta: ${req.url}`);
29
- if (routeInfo) {
30
- console.log(`Ruta definida: ${routeInfo.method} ${routeInfo.path}`);
31
- console.log(`Controlador: ${routeInfo.controller}`);
32
- console.log(`Acción: ${routeInfo.action}`);
33
- }
34
- console.log(`IP Cliente: ${req.ip}`);
35
- console.log(`Headers:`, req.headers);
36
- if (Object.keys(req.params).length > 0) {
37
- console.log(`Parámetros:`, req.params);
38
- }
39
- if (Object.keys(req.query).length > 0) {
40
- console.log(`Query params:`, req.query);
41
- }
42
- if (req.body && Object.keys(req.body).length > 0) {
43
- console.log(`Body:`, req.body);
44
- }
45
- console.log(`-----------------------------\n`);
46
- });
47
-
48
- // Hook para filtrar y modificar la lista de endpoints
49
- hooked.addFilter('endpoints_list', (endpoints) => {
50
- // Agregar información adicional a la lista de endpoints
51
- return endpoints.map(endpoint => ({
52
- ...endpoint,
53
- addedBy: 'hook-system',
54
- timestamp: new Date().toISOString()
55
- }));
56
- });
57
- }
58
- }
59
-
60
- module.exports = AppHooks;
@@ -1,20 +0,0 @@
1
- /**
2
- * Middleware para capturar el body de las solicitudes
3
- * Permite que el body esté disponible para los hooks
4
- */
5
-
6
- const express = require('express');
7
-
8
- // Middleware para capturar el body
9
- const captureBodyMiddleware = express.json({
10
- verify: (req, res, buf, encoding) => {
11
- req.rawBody = buf;
12
- try {
13
- req.capturedBody = JSON.parse(buf.toString(encoding));
14
- } catch (e) {
15
- req.capturedBody = buf.toString(encoding);
16
- }
17
- }
18
- });
19
-
20
- module.exports = { captureBodyMiddleware };