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.
- package/README.md +31 -26
- package/docs/configuration/configuration.md +815 -0
- package/docs/core/core-concepts.md +801 -0
- package/docs/core/error-handling.md +391 -153
- package/docs/core/logging.md +426 -68
- package/docs/modules/body-parsing.md +561 -0
- package/docs/modules/cors.md +369 -0
- package/docs/modules/index.md +125 -0
- package/docs/modules/ip-security.md +280 -0
- package/docs/modules/rate-limiting.md +795 -0
- package/index.d.ts +278 -76
- package/index.js +18 -18
- package/index.js.map +17 -8
- package/package.json +5 -3
- package/docs/configuration/advanced-configuration-options.md +0 -302
- package/docs/configuration/configuration-patterns.md +0 -500
- package/docs/core/context.md +0 -230
- package/docs/core/examples.md +0 -444
- package/docs/core/request.md +0 -161
- package/docs/core/response.md +0 -212
- package/docs/core/routes.md +0 -720
- package/docs/quick-reference.md +0 -346
- package/docs/security/body-parsing.md +0 -296
- package/docs/security/cors.md +0 -189
- package/docs/security/ip-security.md +0 -234
- package/docs/security/security-overview.md +0 -282
- package/docs/start-here.md +0 -184
|
@@ -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
|
-
|
|
5
|
+
For detailed configuration examples and patterns, see [Configuration Guide](../configuration/configuration.md).
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
|
|
30
|
+
<aside>
|
|
25
31
|
|
|
26
|
-
|
|
32
|
+
Options: `automatic`
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
40
|
+
### Custom Error Handler — @default <span style="color: #2ecc71">`built-in`</span>
|
|
37
41
|
|
|
38
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
281
|
+
await app.listen();
|
|
282
|
+
```
|
|
147
283
|
|
|
148
|
-
|
|
284
|
+
### Dev API
|
|
149
285
|
|
|
150
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
293
|
+
const app = new YinzerFlow({ port: 3000 });
|
|
172
294
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
error:
|
|
218
|
-
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
|
|
244
|
-
console.error('Application error:', errorInfo);
|
|
397
|
+
log.info('Fetching user', { id, requestId: ctx.state.requestId });
|
|
245
398
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
##
|
|
416
|
+
## 🚀 Performance Notes
|
|
257
417
|
|
|
258
|
-
- **
|
|
259
|
-
- **
|
|
260
|
-
- **
|
|
261
|
-
- **CORS
|
|
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
|
|
423
|
+
## 🔒 Security Notes
|
|
266
424
|
|
|
267
|
-
YinzerFlow implements several security measures
|
|
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
|
-
|
|
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
|
+
```
|