jerkjs 2.2.0 → 2.3.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.
@@ -0,0 +1,454 @@
1
+ # Guía: Cómo añadir rutas, controladores y vistas sin utilizar routes.json
2
+
3
+ ## Introducción
4
+
5
+ El framework JERK permite definir rutas, controladores y vistas de dos maneras:
6
+
7
+ 1. Usando el archivo `routes.json` (método tradicional)
8
+ 2. Usando el método `addRoute()` directamente en el código (método programático)
9
+
10
+ Esta guía explica cómo implementar la segunda opción, que permite una mayor flexibilidad y control directo sobre la definición de rutas sin depender de archivos JSON externos.
11
+
12
+ ## Ventajas del enfoque sin routes.json
13
+
14
+ - **Mayor control**: Acceso directo al código de definición de rutas
15
+ - **Flexibilidad**: Definición dinámica de rutas basadas en condiciones en tiempo de ejecución
16
+ - **Integración directa**: No dependencia de archivos externos
17
+ - **Compatibilidad**: Todas las funcionalidades disponibles en `routes.json` también están disponibles con `addRoute()`
18
+
19
+ ## Configuración del servidor
20
+
21
+ Para usar el enfoque sin `routes.json`, debes configurar tu servidor para definir rutas directamente:
22
+
23
+ ```javascript
24
+ const { APIServer, Logger, Authenticator, Cors, RateLimiter, Compressor, Firewall, SessionManager, ViewEngine, hooks } = require('jerkjs');
25
+
26
+ class MiServidor {
27
+ constructor() {
28
+ this.server = new APIServer({
29
+ port: 3000,
30
+ host: 'localhost'
31
+ });
32
+
33
+ // Inicializar componentes
34
+ this.logger = new Logger();
35
+ this.authenticator = new Authenticator({ logger: this.logger });
36
+ this.cors = new Cors();
37
+ this.rateLimiter = new RateLimiter();
38
+ this.compressor = new Compressor({ hooks: hooks });
39
+ this.firewall = new Firewall({ logger: this.logger });
40
+ this.sessionManager = new SessionManager();
41
+ this.viewEngine = new ViewEngine({ hooks: hooks });
42
+
43
+ // Configurar el motor de vistas en el servidor
44
+ this.server.viewEngine = this.viewEngine;
45
+ this.viewEngine.viewsPath = './views'; // Ruta a tus vistas
46
+
47
+ // Configurar middlewares
48
+ this.configureMiddlewares();
49
+
50
+ // Definir rutas sin usar routes.json
51
+ this.defineRoutes();
52
+ }
53
+
54
+ configureMiddlewares() {
55
+ this.server.use(this.firewall.middleware());
56
+ this.server.use(this.compressor.middleware());
57
+ this.server.use(this.cors.middleware());
58
+ this.server.use(this.rateLimiter.middleware());
59
+ this.server.use(this.sessionManager.middleware());
60
+ }
61
+
62
+ defineRoutes() {
63
+ // Ruta principal
64
+ this.server.addRoute('GET', '/', (req, res) => {
65
+ res.render('index', {
66
+ title: 'Mi Aplicación',
67
+ message: 'Bienvenido al servidor sin routes.json'
68
+ });
69
+ });
70
+
71
+ // Ruta con autenticación JWT - implementación manual
72
+ this.server.addRoute('GET', '/dashboard', (req, res) => {
73
+ // Verificar token JWT manualmente
74
+ const authHeader = req.headers.authorization;
75
+ const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
76
+
77
+ if (!token) {
78
+ res.writeHead(401, { 'Content-Type': 'application/json' });
79
+ res.end(JSON.stringify({ error: 'Token no proporcionado' }));
80
+ return;
81
+ }
82
+
83
+ // Verificar que JWT_SECRET esté definido en las variables de entorno
84
+ const jwtSecret = process.env.JWT_SECRET;
85
+ if (!jwtSecret) {
86
+ res.writeHead(500, { 'Content-Type': 'application/json' });
87
+ res.end(JSON.stringify({ error: 'Configuración de seguridad incompleta: JWT_SECRET no definido' }));
88
+ return;
89
+ }
90
+
91
+ try {
92
+ const jwt = require('jsonwebtoken');
93
+ const decoded = jwt.verify(token, jwtSecret);
94
+
95
+ // Agregar información del usuario a la solicitud
96
+ req.user = decoded;
97
+
98
+ // Continuar con la lógica protegida
99
+ res.render('dashboard', {
100
+ title: 'Panel de Control',
101
+ user: req.user
102
+ });
103
+ } catch (error) {
104
+ res.writeHead(401, { 'Content-Type': 'application/json' });
105
+ res.end(JSON.stringify({ error: 'Token inválido o expirado' }));
106
+ }
107
+ }, {
108
+ contentType: 'text/html'
109
+ });
110
+
111
+ // Ruta de API con content-type JSON
112
+ this.server.addRoute('GET', '/api/data', (req, res) => {
113
+ res.end(JSON.stringify({
114
+ message: 'Datos de la API',
115
+ timestamp: new Date().toISOString()
116
+ }));
117
+ }, {
118
+ contentType: 'application/json'
119
+ });
120
+ }
121
+
122
+ start() {
123
+ this.server.start();
124
+ }
125
+ }
126
+
127
+ const miServidor = new MiServidor();
128
+ miServidor.start();
129
+ ```
130
+
131
+ ## Definición de rutas con todas las funcionalidades
132
+
133
+ El método `addRoute()` ahora soporta todas las funcionalidades que estaban disponibles en `routes.json`, aunque la implementación difiere ligeramente:
134
+
135
+ ### 1. Rutas básicas
136
+
137
+ ```javascript
138
+ // Ruta simple sin autenticación
139
+ server.addRoute('GET', '/', (req, res) => {
140
+ res.end('Hola Mundo');
141
+ });
142
+ ```
143
+
144
+ ### 2. Rutas con content-type
145
+
146
+ ```javascript
147
+ // Ruta con content-type específico
148
+ server.addRoute('GET', '/api/users', (req, res) => {
149
+ res.end(JSON.stringify({ users: [] }));
150
+ }, {
151
+ contentType: 'application/json'
152
+ });
153
+ ```
154
+
155
+ ### 3. Rutas con autenticación
156
+
157
+ **Importante**: A diferencia de `routes.json`, para usar autenticación con `addRoute()`, debes implementar la lógica de autenticación manualmente en el handler o registrar una estrategia de autenticación en el Authenticator:
158
+
159
+ ```javascript
160
+ // Opción 1: Implementar autenticación JWT manualmente
161
+ server.addRoute('GET', '/protected', (req, res) => {
162
+ // Verificar token JWT manualmente
163
+ const authHeader = req.headers.authorization;
164
+ const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
165
+
166
+ if (!token) {
167
+ res.writeHead(401, { 'Content-Type': 'application/json' });
168
+ res.end(JSON.stringify({ error: 'Token no proporcionado' }));
169
+ return;
170
+ }
171
+
172
+ // Verificar que JWT_SECRET esté definido en las variables de entorno
173
+ const jwtSecret = process.env.JWT_SECRET;
174
+ if (!jwtSecret) {
175
+ res.writeHead(500, { 'Content-Type': 'application/json' });
176
+ res.end(JSON.stringify({ error: 'Configuración de seguridad incompleta: JWT_SECRET no definido' }));
177
+ return;
178
+ }
179
+
180
+ try {
181
+ const jwt = require('jsonwebtoken');
182
+ const decoded = jwt.verify(token, jwtSecret);
183
+
184
+ // Agregar información del usuario a la solicitud
185
+ req.user = decoded;
186
+
187
+ // Continuar con la lógica protegida
188
+ res.render('protected', { user: req.user });
189
+ } catch (error) {
190
+ res.writeHead(401, { 'Content-Type': 'application/json' });
191
+ res.end(JSON.stringify({ error: 'Token inválido o expirado' }));
192
+ }
193
+ }, {
194
+ contentType: 'text/html'
195
+ });
196
+
197
+ // Opción 2: Registrar estrategia de autenticación y usar middleware
198
+ // Registrar la estrategia JWT en el authenticator
199
+ authenticator.use('jwt-custom', async (req, options = {}) => {
200
+ const authHeader = req.headers.authorization;
201
+ const token = authHeader && authHeader.split(' ')[1];
202
+
203
+ if (!token) return false;
204
+
205
+ // Verificar que JWT_SECRET esté definido en las variables de entorno
206
+ const jwtSecret = process.env.JWT_SECRET;
207
+ if (!jwtSecret) {
208
+ console.error('ERROR: JWT_SECRET no está definido en las variables de entorno');
209
+ return false;
210
+ }
211
+
212
+ try {
213
+ const jwt = require('jsonwebtoken');
214
+ const decoded = jwt.verify(token, jwtSecret);
215
+ req.user = decoded;
216
+ return true;
217
+ } catch (error) {
218
+ return false;
219
+ }
220
+ });
221
+
222
+ // Luego usar el middleware de autenticación
223
+ server.addRoute('GET', '/protected',
224
+ authenticator.authenticate('jwt-custom', {})((req, res) => {
225
+ // Esta ruta está protegida
226
+ res.render('protected', { user: req.user });
227
+ }), {
228
+ contentType: 'text/html'
229
+ }
230
+ );
231
+ ```
232
+
233
+ ### 4. Rutas con vistas y variables
234
+
235
+ ```javascript
236
+ // Ruta que renderiza una vista con variables
237
+ server.addRoute('GET', '/profile', (req, res) => {
238
+ res.render('profile', {
239
+ user: { name: 'Juan', email: 'juan@example.com' },
240
+ title: 'Perfil de Usuario'
241
+ });
242
+ });
243
+ ```
244
+
245
+ ## Creación de controladores sin routes.json
246
+
247
+ Puedes crear controladores que extiendan `ControllerBase` y usarlos con el enfoque programático:
248
+
249
+ ```javascript
250
+ // controllers/HomeController.js
251
+ const ControllerBase = require('../lib/mvc/controllerBase');
252
+
253
+ class HomeController extends ControllerBase {
254
+ constructor(options = {}) {
255
+ super(options);
256
+ this.viewsPath = './views'; // Ruta a las vistas
257
+ }
258
+
259
+ index(req, res) {
260
+ this.set('title', 'Página de Inicio');
261
+ this.set('message', 'Bienvenido a mi aplicación');
262
+ this.set('users', [
263
+ { id: 1, name: 'Juan' },
264
+ { id: 2, name: 'Ana' }
265
+ ]);
266
+
267
+ this.render(res, 'home/index');
268
+ }
269
+
270
+ showUser(req, res) {
271
+ const userId = req.params.id;
272
+ this.set('title', `Usuario ${userId}`);
273
+ this.set('user', { id: userId, name: `Usuario ${userId}` });
274
+
275
+ this.render(res, 'user/show');
276
+ }
277
+ }
278
+
279
+ module.exports = new HomeController();
280
+ ```
281
+
282
+ Y luego usarlo en tu servidor:
283
+
284
+ ```javascript
285
+ const HomeController = require('./controllers/HomeController');
286
+
287
+ server.addRoute('GET', '/', (req, res) => {
288
+ HomeController.setRequestResponse(req, res);
289
+ HomeController.index(req, res);
290
+ });
291
+
292
+ server.addRoute('GET', '/users/:id', (req, res) => {
293
+ HomeController.setRequestResponse(req, res);
294
+ HomeController.showUser(req, res);
295
+ });
296
+ ```
297
+
298
+ ## Creación de vistas con variables y estructuras complejas
299
+
300
+ Las vistas pueden usar todas las funcionalidades del motor de plantillas:
301
+
302
+ ### Vista básica con variables
303
+ ```html
304
+ <!-- views/home/index.html -->
305
+ <!DOCTYPE html>
306
+ <html>
307
+ <head>
308
+ <title>{{title}}</title>
309
+ </head>
310
+ <body>
311
+ <h1>{{title}}</h1>
312
+ <p>{{message}}</p>
313
+
314
+ {{if users}}
315
+ <ul>
316
+ {{foreach:users}}
317
+ <li>{{item.name}}</li>
318
+ {{endforeach}}
319
+ </ul>
320
+ {{else}}
321
+ <p>No hay usuarios disponibles.</p>
322
+ {{endif}}
323
+ </body>
324
+ </html>
325
+ ```
326
+
327
+ ### Vista con estructuras condicionales y bucles
328
+ ```html
329
+ <!-- views/user/show.html -->
330
+ <!DOCTYPE html>
331
+ <html>
332
+ <head>
333
+ <title>{{title}}</title>
334
+ </head>
335
+ <body>
336
+ <h1>{{user.name}}</h1>
337
+ <p>ID: {{user.id}}</p>
338
+
339
+ {{if user.active}}
340
+ <span class="active">Activo</span>
341
+ {{else}}
342
+ <span class="inactive">Inactivo</span>
343
+ {{endif}}
344
+ </body>
345
+ </html>
346
+ ```
347
+
348
+ ## Errores comunes y soluciones
349
+
350
+ ### 1. Variables no se reemplazan en vistas
351
+
352
+ **Problema**: Las variables `{{variable}}` no se reemplazan en las vistas.
353
+
354
+ **Solución**: Asegúrate de que:
355
+ - El `ViewEngine` esté correctamente configurado en el servidor
356
+ - La ruta de vistas esté correctamente especificada
357
+ - Las variables se estén pasando correctamente al método `render()`
358
+
359
+ ### 2. Autenticación no funciona con addRoute()
360
+
361
+ **Problema**: La autenticación no se aplica cuando se usa `addRoute()`.
362
+
363
+ **Solución**: A diferencia de `routes.json`, `addRoute()` no acepta directamente las opciones `auth` y `authOptions`. Debes implementar la lógica de autenticación manualmente en el handler o usar el sistema de autenticación del framework como middleware:
364
+
365
+ ```javascript
366
+ // Solución 1: Implementar autenticación manualmente en el handler
367
+ server.addRoute('GET', '/protected', (req, res) => {
368
+ const authHeader = req.headers.authorization;
369
+ const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
370
+
371
+ if (!token) {
372
+ res.writeHead(401, { 'Content-Type': 'application/json' });
373
+ res.end(JSON.stringify({ error: 'Token no proporcionado' }));
374
+ return;
375
+ }
376
+
377
+ try {
378
+ const jwt = require('jsonwebtoken');
379
+ const decoded = jwt.verify(token, process.env.JWT_SECRET);
380
+ req.user = decoded;
381
+
382
+ // Continuar con la lógica protegida
383
+ res.render('protected', { user: req.user });
384
+ } catch (error) {
385
+ res.writeHead(401, { 'Content-Type': 'application/json' });
386
+ res.end(JSON.stringify({ error: 'Token inválido o expirado' }));
387
+ }
388
+ }, {
389
+ contentType: 'text/html'
390
+ });
391
+
392
+ // Solución 2: Usar el sistema de autenticación como middleware
393
+ // Registrar la estrategia de autenticación
394
+ authenticator.use('jwt-middleware', async (req, options = {}) => {
395
+ const authHeader = req.headers.authorization;
396
+ const token = authHeader && authHeader.split(' ')[1];
397
+
398
+ if (!token) return false;
399
+
400
+ // Verificar que JWT_SECRET esté definido en las variables de entorno
401
+ const jwtSecret = process.env.JWT_SECRET;
402
+ if (!jwtSecret) {
403
+ console.error('ERROR: JWT_SECRET no está definido en las variables de entorno');
404
+ return false;
405
+ }
406
+
407
+ try {
408
+ const jwt = require('jsonwebtoken');
409
+ const decoded = jwt.verify(token, jwtSecret);
410
+ req.user = decoded;
411
+ return true;
412
+ } catch (error) {
413
+ return false;
414
+ }
415
+ });
416
+
417
+ // Aplicar el middleware de autenticación a la ruta
418
+ server.addRoute('GET', '/protected',
419
+ authenticator.authenticate('jwt-middleware', {})((req, res) => {
420
+ res.render('protected', { user: req.user });
421
+ }), {
422
+ contentType: 'text/html'
423
+ }
424
+ );
425
+ ```
426
+
427
+ ### 3. Rutas parametrizadas no funcionan
428
+
429
+ **Problema**: Las rutas como `/users/:id` no capturan los parámetros.
430
+
431
+ **Solución**: Los parámetros están disponibles en `req.params`:
432
+
433
+ ```javascript
434
+ server.addRoute('GET', '/users/:id', (req, res) => {
435
+ const userId = req.params.id; // Aquí está el valor del parámetro
436
+ res.end(`Usuario ID: ${userId}`);
437
+ });
438
+ ```
439
+
440
+ ## Buenas prácticas
441
+
442
+ 1. **Organiza tus rutas**: Agrupa rutas relacionadas en funciones separadas
443
+ 2. **Usa controladores**: Extiende `ControllerBase` para lógica compleja
444
+ 3. **Configura correctamente el ViewEngine**: Asegúrate de que la ruta de vistas esté correctamente configurada
445
+ 4. **Maneja errores**: Implementa manejo de errores adecuado
446
+ 5. **Documenta tus rutas**: Aunque no uses `routes.json`, documenta tus rutas programáticas
447
+ 6. **Implementa autenticación manualmente**: Para usar autenticación con `addRoute()`, implementa la lógica manualmente o registra estrategias de autenticación
448
+ 7. **Usa el sistema de hooks**: Aprovecha el sistema de hooks y filters para extensibilidad
449
+
450
+ ## Conclusión
451
+
452
+ El enfoque sin `routes.json` ofrece una alternativa poderosa y flexible para definir rutas en JERK Framework. Con las mejoras implementadas, ahora puedes usar `addRoute()` con todas las funcionalidades equivalentes a las disponibles en `routes.json`, incluyendo autenticación (implementada manualmente o con middleware), content-type, vistas con variables anidadas y más.
453
+
454
+ Esta arquitectura permite una mayor flexibilidad y control sobre la definición de rutas, ideal para aplicaciones que requieren lógica dinámica en la definición de endpoints. Ambas arquitecturas (con y sin `routes.json`) ofrecen las mismas funcionalidades y pueden usarse según las necesidades del proyecto.