yinzerflow 0.4.4 → 0.5.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.
@@ -1,10 +1,16 @@
1
- # Error Handling
1
+ # 📖 Error Handling
2
2
 
3
3
  YinzerFlow provides comprehensive error handling with automatic error catching and custom error handlers. The framework automatically catches all errors thrown in route handlers, hooks, and middleware, ensuring your application never crashes due to unhandled exceptions.
4
4
 
5
- ## Configuration
5
+ For detailed configuration examples and patterns, see [Configuration Guide](../configuration/configuration.md).
6
6
 
7
- Error handling is automatically enabled and requires no configuration for basic usage:
7
+ # ⚙️ Usage
8
+
9
+ ## 🎛️ Settings
10
+
11
+ ### Automatic Error Catching — @default <span style="color: #2ecc71">`enabled`</span>
12
+
13
+ Error handling is automatically enabled and requires no configuration for basic usage.
8
14
 
9
15
  ```typescript
10
16
  import { YinzerFlow } from 'yinzerflow';
@@ -21,43 +27,21 @@ app.get('/api/users/:id', async ({ request }) => {
21
27
  });
22
28
  ```
23
29
 
24
- ### Configuration Options
30
+ <aside>
25
31
 
26
- YinzerFlow's error handling includes built-in security and logging features:
32
+ Options: `automatic`
27
33
 
28
- | Feature | Default | Description |
29
- |---------|---------|-------------|
30
- | **Automatic Error Catching** | Enabled | All errors in handlers are automatically caught |
31
- | **Default Error Response** | 500 Internal Server Error | Structured error response with logging |
32
- | **Error Logging** | Enabled | All errors logged with Pittsburgh personality |
33
- | **CORS Headers** | Automatic | Error responses include proper CORS headers |
34
- | **Fallback Protection** | Enabled | Safe fallback if custom handlers fail |
34
+ - Error handling is enabled automatically
35
+ - All errors in handlers, hooks, and middleware are caught
36
+ - Default error response: 500 Internal Server Error
37
+ - Automatic logging with Pittsburgh personality
38
+ </aside>
35
39
 
36
- ## Examples
40
+ ### Custom Error Handler — @default <span style="color: #2ecc71">`built-in`</span>
37
41
 
38
- ### Basic Example
42
+ Register a custom error handler for all routes.
39
43
 
40
44
  ```typescript
41
- import { YinzerFlow } from 'yinzerflow';
42
-
43
- const app = new YinzerFlow({ port: 3000 });
44
-
45
- // No try-catch needed - errors are automatically handled
46
- app.get('/api/users/:id', async (ctx) => {
47
- const userId = ctx.request.params.id;
48
-
49
- // Database errors are automatically caught
50
- const user = await database.findUser(userId);
51
-
52
- if (!user) {
53
- // This error will be handled by your error handler
54
- throw new Error('User not found');
55
- }
56
-
57
- return user;
58
- });
59
-
60
- // Custom error handler
61
45
  app.onError((ctx, error) => {
62
46
  ctx.response.setStatusCode(500);
63
47
  return {
@@ -66,21 +50,114 @@ app.onError((ctx, error) => {
66
50
  timestamp: new Date().toISOString()
67
51
  };
68
52
  });
53
+ ```
69
54
 
70
- await app.listen();
55
+ <aside>
56
+
57
+ Options: `HandlerCallback | built-in`
58
+
59
+ - `built-in`: Default error handler with logging
60
+ - Custom: User-defined error handler function
61
+ - Receives context and error parameters
62
+ - Must return response object
63
+ </aside>
64
+
65
+ ### Custom Not Found Handler — @default <span style="color: #2ecc71">`built-in`</span>
66
+
67
+ Register a custom not-found handler for unmatched routes.
68
+
69
+ ```typescript
70
+ app.onNotFound((ctx) => {
71
+ ctx.response.setStatusCode(404);
72
+ return {
73
+ success: false,
74
+ error: 'Route not found',
75
+ path: ctx.request.path,
76
+ method: ctx.request.method
77
+ };
78
+ });
71
79
  ```
72
80
 
73
- See [Error Handler Pattern](./examples.md#error-handler-pattern) for a complete error handler example.
81
+ <aside>
82
+
83
+ Options: `HandlerCallback | built-in`
84
+
85
+ - `built-in`: Default 404 Not Found response
86
+ - Custom: User-defined not-found handler function
87
+ - Receives context parameter
88
+ - Must return response object
89
+ </aside>
90
+
91
+ # ✨ Best Practices
92
+
93
+ - **Use structured error responses** - Consistent error format across your API
94
+ - **Handle different error types** - Check error instances for specific handling
95
+ - **Log errors for debugging** - Use error handlers for logging and monitoring
96
+ - **Set appropriate status codes** - Use correct HTTP status codes for different errors
97
+ - **Sanitize error information** - Don't expose sensitive data to clients
98
+ - **Use async error handlers** - Error handlers can be async functions
74
99
 
75
- ### Advanced Example
100
+ # 💻 Examples
101
+
102
+ ### Production API
103
+
104
+ **Use Case:** Production API with comprehensive error handling and monitoring
105
+
106
+ **Description:** Production-ready error handling with structured responses, error logging, monitoring integration, and comprehensive error type handling for maximum reliability and debugging capability.
76
107
 
77
108
  ```typescript
78
109
  import { YinzerFlow } from 'yinzerflow';
110
+ import type { HandlerCallback } from 'yinzerflow';
111
+
112
+ // Custom error classes
113
+ class ValidationError extends Error {
114
+ constructor(message: string, public details: any) {
115
+ super(message);
116
+ this.name = 'ValidationError';
117
+ }
118
+ }
119
+
120
+ class DatabaseError extends Error {
121
+ constructor(message: string, public code: string) {
122
+ super(message);
123
+ this.name = 'DatabaseError';
124
+ }
125
+ }
126
+
127
+ class AuthenticationError extends Error {
128
+ constructor(message: string, public code: string) {
129
+ super(message);
130
+ this.name = 'AuthenticationError';
131
+ }
132
+ }
79
133
 
80
134
  const app = new YinzerFlow({ port: 3000 });
81
135
 
82
- // Custom error handler with different error types
83
- app.onError((ctx, error) => {
136
+ // Production error handler with comprehensive error handling
137
+ app.onError(async (ctx, error) => {
138
+ const requestId = ctx.state.requestId || 'unknown';
139
+ const timestamp = new Date().toISOString();
140
+
141
+ // Log detailed error information for monitoring
142
+ const errorInfo = {
143
+ requestId,
144
+ timestamp,
145
+ error: {
146
+ name: error instanceof Error ? error.name : 'Unknown',
147
+ message: error instanceof Error ? error.message : String(error),
148
+ stack: error instanceof Error ? error.stack : undefined
149
+ },
150
+ request: {
151
+ method: ctx.request.method,
152
+ path: ctx.request.path,
153
+ ipAddress: ctx.request.ipAddress,
154
+ userAgent: ctx.request.headers['user-agent']
155
+ }
156
+ };
157
+
158
+ // Log to monitoring service (but don't expose to client)
159
+ console.error('Application error:', errorInfo);
160
+
84
161
  // Handle different error types
85
162
  if (error instanceof ValidationError) {
86
163
  ctx.response.setStatusCode(400);
@@ -88,7 +165,8 @@ app.onError((ctx, error) => {
88
165
  success: false,
89
166
  error: 'Validation failed',
90
167
  details: error.details,
91
- path: ctx.request.path
168
+ requestId,
169
+ timestamp
92
170
  };
93
171
  }
94
172
 
@@ -97,7 +175,10 @@ app.onError((ctx, error) => {
97
175
  return {
98
176
  success: false,
99
177
  error: 'Service temporarily unavailable',
100
- retryAfter: 30
178
+ code: error.code,
179
+ retryAfter: 60,
180
+ requestId,
181
+ timestamp
101
182
  };
102
183
  }
103
184
 
@@ -106,7 +187,9 @@ app.onError((ctx, error) => {
106
187
  return {
107
188
  success: false,
108
189
  error: 'Authentication required',
109
- code: 'UNAUTHORIZED'
190
+ code: error.code,
191
+ requestId,
192
+ timestamp
110
193
  };
111
194
  }
112
195
 
@@ -115,156 +198,231 @@ app.onError((ctx, error) => {
115
198
  return {
116
199
  success: false,
117
200
  error: 'Internal server error',
118
- requestId: ctx.request.headers['x-request-id'] || 'unknown'
201
+ requestId,
202
+ timestamp,
203
+ message: process.env.NODE_ENV === 'production' ? 'Something went wrong' : errorInfo.error.message
119
204
  };
120
205
  });
121
206
 
122
- // Custom not found handler
123
- app.onNotFound((ctx) => {
207
+ // Custom not-found handler
208
+ app.onNotFound(async (ctx) => {
209
+ const requestId = ctx.state.requestId || 'unknown';
210
+
124
211
  ctx.response.setStatusCode(404);
125
212
  return {
126
213
  success: false,
127
214
  error: 'Route not found',
128
215
  path: ctx.request.path,
129
216
  method: ctx.request.method,
130
- availableMethods: ['GET', 'POST', 'PUT', 'DELETE']
217
+ requestId,
218
+ timestamp: new Date().toISOString(),
219
+ availableEndpoints: [
220
+ '/api/users',
221
+ '/api/posts',
222
+ '/health'
223
+ ],
224
+ documentation: 'https://api.example.com/docs'
131
225
  };
132
226
  });
133
227
 
134
- await app.listen();
135
- ```
136
-
137
- ## Common Use Cases
228
+ // Example route with error handling
229
+ app.get('/api/users/:id', async (ctx) => {
230
+ const { id } = ctx.request.params;
231
+
232
+ // Validate UUID format
233
+ if (!isValidUUID(id)) {
234
+ throw new ValidationError('Invalid user ID format', { field: 'id', value: id });
235
+ }
236
+
237
+ try {
238
+ const user = await getUserById(id);
239
+ if (!user) {
240
+ throw new Error('User not found');
241
+ }
242
+
243
+ return { success: true, user };
244
+ } catch (error) {
245
+ if (error instanceof DatabaseError) {
246
+ throw error; // Re-throw to be handled by error handler
247
+ }
248
+
249
+ // Wrap generic errors
250
+ throw new Error(`Failed to fetch user: ${error.message}`);
251
+ }
252
+ });
138
253
 
139
- - **Database Error Handling**: Automatically catch and handle database connection errors, query failures, and constraint violations
140
- - **Validation Error Processing**: Handle input validation errors with structured error responses and field-specific messages
141
- - **Authentication Error Management**: Process authentication failures, expired tokens, and authorization errors with appropriate status codes
142
- - **External API Error Handling**: Catch and handle errors from third-party services with retry logic and fallback responses
143
- - **File Upload Error Processing**: Handle file size limits, type validation errors, and upload failures with user-friendly messages
144
- - **Rate Limiting Error Responses**: Process rate limit violations with appropriate headers and retry information
254
+ // Authentication route with error handling
255
+ app.post('/api/auth/login', async (ctx) => {
256
+ const { username, password } = ctx.request.body as { username: string; password: string };
257
+
258
+ if (!username || !password) {
259
+ throw new ValidationError('Username and password are required', {
260
+ fields: ['username', 'password']
261
+ });
262
+ }
263
+
264
+ try {
265
+ const user = await authenticateUser(username, password);
266
+ if (!user) {
267
+ throw new AuthenticationError('Invalid credentials', 'INVALID_CREDENTIALS');
268
+ }
269
+
270
+ const token = generateToken(user);
271
+ return { success: true, token, user: { id: user.id, username: user.username } };
272
+ } catch (error) {
273
+ if (error instanceof AuthenticationError) {
274
+ throw error; // Re-throw to be handled by error handler
275
+ }
276
+
277
+ throw new DatabaseError('Authentication service unavailable', 'AUTH_SERVICE_DOWN');
278
+ }
279
+ });
145
280
 
146
- ## Error Handler Parameters
281
+ await app.listen();
282
+ ```
147
283
 
148
- The error handler receives two parameters:
284
+ ### Dev API
149
285
 
150
- ### Context Object (`ctx`)
151
- The complete request context containing `request` and `response` objects. See [Context Object Documentation](./context.md) for detailed information about accessing context properties.
286
+ **Use Case:** Development server with detailed error logging and debugging
152
287
 
153
- ### Error Object (`error`)
154
- The caught error can be any type - Error instances, strings, objects, or other values:
288
+ **Description:** Development configuration with extensive error logging, detailed error information, and simplified error handling for easier debugging and development.
155
289
 
156
290
  ```typescript
157
- app.onError((ctx, error) => {
158
- // Handle different error types
159
- if (error instanceof Error) {
160
- return { error: error.message, stack: error.stack };
161
- } else if (typeof error === 'string') {
162
- return { error };
163
- } else if (typeof error === 'object' && error !== null) {
164
- return { error: JSON.stringify(error) };
165
- } else {
166
- return { error: 'Unknown error occurred' };
167
- }
168
- });
169
- ```
291
+ import { YinzerFlow, log } from 'yinzerflow';
170
292
 
171
- ## Error Handler Best Practices
293
+ const app = new YinzerFlow({ port: 3000 });
172
294
 
173
- ### Structured Error Responses
174
- ```typescript
175
- app.onError((ctx, error) => {
176
- const errorResponse = {
295
+ // Development error handler with detailed logging
296
+ app.onError(async (ctx, error) => {
297
+ const requestId = ctx.state.requestId || 'unknown';
298
+
299
+ // Log comprehensive error information
300
+ log.error('Error occurred in development', {
301
+ requestId,
302
+ error: {
303
+ name: error instanceof Error ? error.name : 'Unknown',
304
+ message: error instanceof Error ? error.message : String(error),
305
+ stack: error instanceof Error ? error.stack : undefined
306
+ },
307
+ request: {
308
+ method: ctx.request.method,
309
+ path: ctx.request.path,
310
+ headers: ctx.request.headers,
311
+ query: ctx.request.query,
312
+ params: ctx.request.params,
313
+ body: ctx.request.body,
314
+ ipAddress: ctx.request.ipAddress
315
+ },
316
+ state: ctx.state
317
+ });
318
+
319
+ // Use table logging for structured data
320
+ log.table({
321
+ error: error instanceof Error ? error.message : String(error),
322
+ requestId,
323
+ method: ctx.request.method,
324
+ path: ctx.request.path,
325
+ timestamp: new Date().toISOString()
326
+ }, 'Error Details');
327
+
328
+ // Simple error response for development
329
+ ctx.response.setStatusCode(500);
330
+ return {
177
331
  success: false,
178
- message: error instanceof Error ? error.message : 'Internal server error',
332
+ error: 'Development error',
333
+ message: error instanceof Error ? error.message : String(error),
334
+ stack: error instanceof Error ? error.stack : undefined,
335
+ requestId,
179
336
  timestamp: new Date().toISOString(),
337
+ debug: {
338
+ method: ctx.request.method,
339
+ path: ctx.request.path,
340
+ query: ctx.request.query,
341
+ params: ctx.request.params
342
+ }
343
+ };
344
+ });
345
+
346
+ // Development not-found handler
347
+ app.onNotFound(async (ctx) => {
348
+ log.warn('Route not found', {
349
+ method: ctx.request.method,
350
+ path: ctx.request.path,
351
+ ipAddress: ctx.request.ipAddress
352
+ });
353
+
354
+ ctx.response.setStatusCode(404);
355
+ return {
356
+ success: false,
357
+ error: 'Route not found',
180
358
  path: ctx.request.path,
181
359
  method: ctx.request.method,
182
- requestId: ctx.request.headers['x-request-id'] || 'unknown'
360
+ timestamp: new Date().toISOString(),
361
+ debug: {
362
+ availableRoutes: [
363
+ 'GET /api/users',
364
+ 'POST /api/users',
365
+ 'GET /api/users/:id',
366
+ 'PUT /api/users/:id',
367
+ 'DELETE /api/users/:id'
368
+ ],
369
+ query: ctx.request.query,
370
+ headers: ctx.request.headers
371
+ }
183
372
  };
184
-
185
- ctx.response.setStatusCode(500);
186
- return errorResponse;
187
373
  });
188
- ```
189
374
 
190
- ### Error Type Handling
191
- ```typescript
192
- app.onError((ctx, error) => {
193
- // Handle specific error types
194
- if (error instanceof ValidationError) {
195
- ctx.response.setStatusCode(400);
196
- return {
197
- success: false,
198
- error: 'Validation failed',
199
- details: error.details,
200
- fields: error.fields
201
- };
202
- }
203
-
204
- if (error instanceof DatabaseError) {
205
- ctx.response.setStatusCode(503);
206
- return {
207
- success: false,
208
- error: 'Database connection failed',
209
- retryAfter: 60
210
- };
211
- }
375
+ // Test route that throws different types of errors
376
+ app.get('/test/error/:type', async (ctx) => {
377
+ const { type } = ctx.request.params;
212
378
 
213
- if (error instanceof RateLimitError) {
214
- ctx.response.setStatusCode(429);
215
- return {
216
- success: false,
217
- error: 'Too many requests',
218
- retryAfter: error.retryAfter
219
- };
379
+ switch (type) {
380
+ case 'validation':
381
+ throw new Error('Validation error: Invalid input');
382
+ case 'database':
383
+ throw new Error('Database error: Connection failed');
384
+ case 'auth':
385
+ throw new Error('Authentication error: Invalid token');
386
+ case 'generic':
387
+ throw new Error('Generic error occurred');
388
+ default:
389
+ return { message: 'No error thrown', type };
220
390
  }
221
-
222
- // Default handling
223
- ctx.response.setStatusCode(500);
224
- return { success: false, error: 'Internal server error' };
225
391
  });
226
- ```
227
392
 
228
- ### Error Logging and Monitoring
229
- ```typescript
230
- app.onError((ctx, error) => {
231
- // Log detailed error information for debugging
232
- const errorInfo = {
233
- message: error instanceof Error ? error.message : String(error),
234
- stack: error instanceof Error ? error.stack : undefined,
235
- path: ctx.request.path,
236
- method: ctx.request.method,
237
- userAgent: ctx.request.headers['user-agent'],
238
- ipAddress: ctx.request.ipAddress,
239
- timestamp: new Date().toISOString(),
240
- requestId: ctx.request.headers['x-request-id'] || 'unknown'
241
- };
393
+ // Route that demonstrates error handling
394
+ app.get('/api/users/:id', async (ctx) => {
395
+ const { id } = ctx.request.params;
242
396
 
243
- // Log to monitoring service (but don't expose to client)
244
- console.error('Application error:', errorInfo);
397
+ log.info('Fetching user', { id, requestId: ctx.state.requestId });
245
398
 
246
- // Return sanitized response to client
247
- ctx.response.setStatusCode(500);
248
- return {
249
- success: false,
250
- error: 'Internal server error',
251
- requestId: errorInfo.requestId
252
- };
399
+ try {
400
+ const user = await getUserById(id);
401
+ if (!user) {
402
+ throw new Error(`User with ID ${id} not found`);
403
+ }
404
+
405
+ log.info('User fetched successfully', { userId: user.id });
406
+ return { success: true, user };
407
+ } catch (error) {
408
+ log.error('Failed to fetch user', { id, error: error.message });
409
+ throw error; // Re-throw to be handled by error handler
410
+ }
253
411
  });
412
+
413
+ await app.listen();
254
414
  ```
255
415
 
256
- ## Error Handler Limitations
416
+ ## 🚀 Performance Notes
257
417
 
258
- - **No Error Throwing**: Error handlers should not throw errors - use return statements instead
259
- - **Response Object**: Return a response object that will be sent to the client
260
- - **Status Code**: Set the status code on `ctx.response.setStatusCode()` before returning
261
- - **CORS Headers**: CORS headers are automatically added to error responses
262
- - **Async Support**: Error handlers can be async functions
263
- - **Hook Integration**: Error handlers work with before/after hooks
418
+ - **Automatic error catching**: No performance overhead for error handling
419
+ - **Early error detection**: Errors are caught immediately when thrown
420
+ - **Fallback protection**: Built-in fallback ensures application stability
421
+ - **CORS integration**: Error responses automatically include CORS headers
264
422
 
265
- ## Security Considerations
423
+ ## 🔒 Security Notes
266
424
 
267
- YinzerFlow implements several security measures to prevent common error handling vulnerabilities:
425
+ YinzerFlow implements several security measures for error handling:
268
426
 
269
427
  ### 🛡️ Error Information Sanitization
270
428
  - **Problem**: Detailed error information can expose sensitive data like database credentials, file paths, or internal structure
@@ -290,4 +448,84 @@ YinzerFlow implements several security measures to prevent common error handling
290
448
  - **Problem**: Malicious error handlers could modify responses or bypass security controls
291
449
  - **YinzerFlow Solution**: Error handlers are isolated and failures don't affect core functionality
292
450
 
293
- These security measures ensure YinzerFlow's error handling implementation follows security best practices and prevents common attack vectors while maintaining application stability and user experience.
451
+ ## 🔧 Troubleshooting
452
+
453
+ ### Error Handler Not Working
454
+ - **Problem**: Custom error handler not being called
455
+ - **Fix**: Check error handler registration and function signature
456
+
457
+ ```typescript
458
+ // ❌ Wrong - error handler throws error
459
+ app.onError((ctx, error) => {
460
+ throw new Error('Handler error'); // This will cause fallback
461
+ });
462
+
463
+ // ✅ Correct - error handler returns response
464
+ app.onError((ctx, error) => {
465
+ ctx.response.setStatusCode(500);
466
+ return { error: 'Something went wrong' };
467
+ });
468
+ ```
469
+
470
+ ### Error Handler Causing Crashes
471
+ - **Problem**: Error handler itself throws errors
472
+ - **Fix**: Use try-catch in error handlers or return safe responses
473
+
474
+ ```typescript
475
+ // ❌ Wrong - error handler can throw
476
+ app.onError((ctx, error) => {
477
+ const result = riskyOperation(); // Can throw
478
+ return result;
479
+ });
480
+
481
+ // ✅ Correct - safe error handler
482
+ app.onError((ctx, error) => {
483
+ try {
484
+ const result = riskyOperation();
485
+ return result;
486
+ } catch (handlerError) {
487
+ return { error: 'Error handler failed' };
488
+ }
489
+ });
490
+ ```
491
+
492
+ ### Not Found Handler Not Working
493
+ - **Problem**: Custom not-found handler not being called
494
+ - **Fix**: Check handler registration and route matching
495
+
496
+ ```typescript
497
+ // ❌ Wrong - handler throws error
498
+ app.onNotFound((ctx) => {
499
+ throw new Error('Not found'); // This will cause fallback
500
+ });
501
+
502
+ // ✅ Correct - handler returns response
503
+ app.onNotFound((ctx) => {
504
+ ctx.response.setStatusCode(404);
505
+ return { error: 'Not found' };
506
+ });
507
+ ```
508
+
509
+ ### Error Information Exposed
510
+ - **Problem**: Sensitive information exposed in error responses
511
+ - **Fix**: Sanitize error information before returning to client
512
+
513
+ ```typescript
514
+ // ❌ Wrong - exposing sensitive information
515
+ app.onError((ctx, error) => {
516
+ return {
517
+ error: error.message,
518
+ stack: error.stack, // Exposes internal structure
519
+ database: error.database // Exposes sensitive data
520
+ };
521
+ });
522
+
523
+ // ✅ Correct - sanitized error response
524
+ app.onError((ctx, error) => {
525
+ return {
526
+ error: 'Internal server error',
527
+ requestId: ctx.state.requestId,
528
+ timestamp: new Date().toISOString()
529
+ };
530
+ });
531
+ ```