yinzerflow 0.2.8 → 0.2.9
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/docs/{advanced-configuration-options.md → configuration/advanced-configuration-options.md} +2 -0
- package/docs/configuration/configuration-patterns.md +500 -0
- package/docs/core/context.md +92 -0
- package/docs/core/error-handling.md +293 -0
- package/docs/core/examples.md +195 -0
- package/docs/{request.md → core/request.md} +3 -36
- package/docs/{response.md → core/response.md} +1 -40
- package/docs/{routes.md → core/routes.md} +17 -116
- package/docs/quick-reference.md +346 -0
- package/docs/{body-parsing.md → security/body-parsing.md} +3 -1
- package/docs/{cors.md → security/cors.md} +3 -1
- package/docs/{ip-security.md → security/ip-security.md} +3 -1
- package/docs/security/security-overview.md +282 -0
- package/docs/start-here.md +14 -8
- package/example/app/handlers/example.ts +2 -0
- package/example/app/index.ts +7 -7
- package/index.d.ts +1 -1
- package/index.js +4 -4
- package/index.js.map +6 -6
- package/package.json +1 -1
- /package/docs/{logging.md → security/logging.md} +0 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# Error Handling
|
|
2
|
+
|
|
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
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
Error handling is automatically enabled and requires no configuration for basic usage:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { YinzerFlow } from 'yinzerflow';
|
|
11
|
+
|
|
12
|
+
const app = new YinzerFlow({ port: 3000 });
|
|
13
|
+
|
|
14
|
+
// Errors are automatically caught - no configuration needed
|
|
15
|
+
app.get('/api/users/:id', async ({ request }) => {
|
|
16
|
+
const user = await database.findUser(request.params.id);
|
|
17
|
+
if (!user) {
|
|
18
|
+
throw new Error('User not found'); // Automatically handled
|
|
19
|
+
}
|
|
20
|
+
return user;
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Configuration Options
|
|
25
|
+
|
|
26
|
+
YinzerFlow's error handling includes built-in security and logging features:
|
|
27
|
+
|
|
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 |
|
|
35
|
+
|
|
36
|
+
## Examples
|
|
37
|
+
|
|
38
|
+
### Basic Example
|
|
39
|
+
|
|
40
|
+
```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
|
+
app.onError((ctx, error) => {
|
|
62
|
+
ctx.response.setStatusCode(500);
|
|
63
|
+
return {
|
|
64
|
+
success: false,
|
|
65
|
+
message: 'Something went wrong',
|
|
66
|
+
timestamp: new Date().toISOString()
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await app.listen();
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
See [Error Handler Pattern](./examples.md#error-handler-pattern) for a complete error handler example.
|
|
74
|
+
|
|
75
|
+
### Advanced Example
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { YinzerFlow } from 'yinzerflow';
|
|
79
|
+
|
|
80
|
+
const app = new YinzerFlow({ port: 3000 });
|
|
81
|
+
|
|
82
|
+
// Custom error handler with different error types
|
|
83
|
+
app.onError((ctx, error) => {
|
|
84
|
+
// Handle different error types
|
|
85
|
+
if (error instanceof ValidationError) {
|
|
86
|
+
ctx.response.setStatusCode(400);
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
error: 'Validation failed',
|
|
90
|
+
details: error.details,
|
|
91
|
+
path: ctx.request.path
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (error instanceof DatabaseError) {
|
|
96
|
+
ctx.response.setStatusCode(503);
|
|
97
|
+
return {
|
|
98
|
+
success: false,
|
|
99
|
+
error: 'Service temporarily unavailable',
|
|
100
|
+
retryAfter: 30
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (error instanceof AuthenticationError) {
|
|
105
|
+
ctx.response.setStatusCode(401);
|
|
106
|
+
return {
|
|
107
|
+
success: false,
|
|
108
|
+
error: 'Authentication required',
|
|
109
|
+
code: 'UNAUTHORIZED'
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Default error handling
|
|
114
|
+
ctx.response.setStatusCode(500);
|
|
115
|
+
return {
|
|
116
|
+
success: false,
|
|
117
|
+
error: 'Internal server error',
|
|
118
|
+
requestId: ctx.request.headers['x-request-id'] || 'unknown'
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Custom not found handler
|
|
123
|
+
app.onNotFound((ctx) => {
|
|
124
|
+
ctx.response.setStatusCode(404);
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
error: 'Route not found',
|
|
128
|
+
path: ctx.request.path,
|
|
129
|
+
method: ctx.request.method,
|
|
130
|
+
availableMethods: ['GET', 'POST', 'PUT', 'DELETE']
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
await app.listen();
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Common Use Cases
|
|
138
|
+
|
|
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
|
|
145
|
+
|
|
146
|
+
## Error Handler Parameters
|
|
147
|
+
|
|
148
|
+
The error handler receives two parameters:
|
|
149
|
+
|
|
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.
|
|
152
|
+
|
|
153
|
+
### Error Object (`error`)
|
|
154
|
+
The caught error can be any type - Error instances, strings, objects, or other values:
|
|
155
|
+
|
|
156
|
+
```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
|
+
```
|
|
170
|
+
|
|
171
|
+
## Error Handler Best Practices
|
|
172
|
+
|
|
173
|
+
### Structured Error Responses
|
|
174
|
+
```typescript
|
|
175
|
+
app.onError((ctx, error) => {
|
|
176
|
+
const errorResponse = {
|
|
177
|
+
success: false,
|
|
178
|
+
message: error instanceof Error ? error.message : 'Internal server error',
|
|
179
|
+
timestamp: new Date().toISOString(),
|
|
180
|
+
path: ctx.request.path,
|
|
181
|
+
method: ctx.request.method,
|
|
182
|
+
requestId: ctx.request.headers['x-request-id'] || 'unknown'
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
ctx.response.setStatusCode(500);
|
|
186
|
+
return errorResponse;
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
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
|
+
}
|
|
212
|
+
|
|
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
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Default handling
|
|
223
|
+
ctx.response.setStatusCode(500);
|
|
224
|
+
return { success: false, error: 'Internal server error' };
|
|
225
|
+
});
|
|
226
|
+
```
|
|
227
|
+
|
|
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
|
+
};
|
|
242
|
+
|
|
243
|
+
// Log to monitoring service (but don't expose to client)
|
|
244
|
+
console.error('Application error:', errorInfo);
|
|
245
|
+
|
|
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
|
+
};
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Error Handler Limitations
|
|
257
|
+
|
|
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
|
|
264
|
+
|
|
265
|
+
## Security Considerations
|
|
266
|
+
|
|
267
|
+
YinzerFlow implements several security measures to prevent common error handling vulnerabilities:
|
|
268
|
+
|
|
269
|
+
### 🛡️ Error Information Sanitization
|
|
270
|
+
- **Problem**: Detailed error information can expose sensitive data like database credentials, file paths, or internal structure
|
|
271
|
+
- **YinzerFlow Solution**: Error handlers receive the raw error but should return sanitized responses to clients
|
|
272
|
+
|
|
273
|
+
### 🛡️ Stack Trace Protection
|
|
274
|
+
- **Problem**: Stack traces in production can reveal application structure and sensitive information
|
|
275
|
+
- **YinzerFlow Solution**: Error handlers can access stack traces for logging but should not return them to clients
|
|
276
|
+
|
|
277
|
+
### 🛡️ Error Logging Security
|
|
278
|
+
- **Problem**: Logged errors can contain sensitive information that could be exploited
|
|
279
|
+
- **YinzerFlow Solution**: Built-in logging with Pittsburgh personality, custom loggers can implement additional sanitization
|
|
280
|
+
|
|
281
|
+
### 🛡️ CORS Error Response Protection
|
|
282
|
+
- **Problem**: Error responses without CORS headers can cause client-side issues
|
|
283
|
+
- **YinzerFlow Solution**: CORS headers are automatically added to all error responses
|
|
284
|
+
|
|
285
|
+
### 🛡️ Fallback Error Protection
|
|
286
|
+
- **Problem**: If custom error handlers fail, applications can crash or expose sensitive information
|
|
287
|
+
- **YinzerFlow Solution**: Built-in fallback error handler ensures graceful degradation
|
|
288
|
+
|
|
289
|
+
### 🛡️ Error Handler Isolation
|
|
290
|
+
- **Problem**: Malicious error handlers could modify responses or bypass security controls
|
|
291
|
+
- **YinzerFlow Solution**: Error handlers are isolated and failures don't affect core functionality
|
|
292
|
+
|
|
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.
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Common Examples
|
|
2
|
+
|
|
3
|
+
This file contains shared examples that are referenced throughout the YinzerFlow documentation to avoid duplication.
|
|
4
|
+
|
|
5
|
+
## Basic Handler Pattern
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { YinzerFlow } from 'yinzerflow';
|
|
9
|
+
|
|
10
|
+
const app = new YinzerFlow({ port: 3000 });
|
|
11
|
+
|
|
12
|
+
app.post('/api/users/:id', (ctx) => {
|
|
13
|
+
// Access request properties
|
|
14
|
+
const userId = ctx.request.params.id;
|
|
15
|
+
const userData = ctx.request.body;
|
|
16
|
+
const includeProfile = ctx.request.query.include_profile;
|
|
17
|
+
const authHeader = ctx.request.headers['authorization'];
|
|
18
|
+
const clientIp = ctx.request.ipAddress;
|
|
19
|
+
|
|
20
|
+
// Control response
|
|
21
|
+
ctx.response.setStatusCode(201);
|
|
22
|
+
ctx.response.addHeaders({
|
|
23
|
+
'Location': `/api/users/${userId}`,
|
|
24
|
+
'X-User-ID': userId
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
id: userId,
|
|
29
|
+
data: userData,
|
|
30
|
+
includeProfile: !!includeProfile,
|
|
31
|
+
clientIp
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Request Access Pattern
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
app.get('/api/users/:id', (ctx) => {
|
|
40
|
+
const { request } = ctx;
|
|
41
|
+
|
|
42
|
+
// Access route parameters
|
|
43
|
+
const userId = request.params.id;
|
|
44
|
+
|
|
45
|
+
// Access query parameters
|
|
46
|
+
const includeProfile = request.query.include_profile;
|
|
47
|
+
|
|
48
|
+
// Access headers
|
|
49
|
+
const contentType = request.headers['content-type'];
|
|
50
|
+
const authorization = request.headers['authorization'];
|
|
51
|
+
|
|
52
|
+
// Access request body
|
|
53
|
+
const userData = request.body;
|
|
54
|
+
|
|
55
|
+
// Access raw body for manual parsing when needed
|
|
56
|
+
const rawBody = request.rawBody;
|
|
57
|
+
|
|
58
|
+
const clientIp = request.ipAddress
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
message: 'Request processed successfully',
|
|
62
|
+
userId,
|
|
63
|
+
includeProfile,
|
|
64
|
+
contentType,
|
|
65
|
+
hasAuth: !!authorization,
|
|
66
|
+
receivedData: userData
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Response Control Pattern
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
app.get('/api/users/:id', (ctx) => {
|
|
75
|
+
const { request, response } = ctx;
|
|
76
|
+
const userId = request.params.id;
|
|
77
|
+
|
|
78
|
+
// Set successful status code
|
|
79
|
+
response.setStatusCode(200);
|
|
80
|
+
|
|
81
|
+
// Add custom headers
|
|
82
|
+
response.addHeaders({
|
|
83
|
+
'X-User-ID': userId,
|
|
84
|
+
'Cache-Control': 'max-age=3600',
|
|
85
|
+
'X-API-Version': '1.0'
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Return JSON response body (Content-Type automatically set)
|
|
89
|
+
return {
|
|
90
|
+
id: userId,
|
|
91
|
+
name: 'John Doe',
|
|
92
|
+
email: 'john@example.com',
|
|
93
|
+
timestamp: new Date().toISOString()
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Error Handler Pattern
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
app.onError((ctx, error) => {
|
|
102
|
+
// Access request context in error handler
|
|
103
|
+
const { path, method, headers } = ctx.request;
|
|
104
|
+
|
|
105
|
+
// Control error response
|
|
106
|
+
ctx.response.setStatusCode(500);
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
error: 'Internal server error',
|
|
111
|
+
path,
|
|
112
|
+
method,
|
|
113
|
+
timestamp: new Date().toISOString()
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Hook Pattern
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// Before hook
|
|
122
|
+
app.beforeAll([(ctx) => {
|
|
123
|
+
// Access request context
|
|
124
|
+
const authHeader = ctx.request.headers['authorization'];
|
|
125
|
+
|
|
126
|
+
// Validate authentication
|
|
127
|
+
if (!authHeader) {
|
|
128
|
+
throw new Error('Authentication required');
|
|
129
|
+
}
|
|
130
|
+
}]);
|
|
131
|
+
|
|
132
|
+
// After hook
|
|
133
|
+
app.afterAll([(ctx) => {
|
|
134
|
+
// Access response context
|
|
135
|
+
ctx.response.addHeaders({
|
|
136
|
+
'X-Response-Time': Date.now().toString()
|
|
137
|
+
});
|
|
138
|
+
}]);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## TypeScript Pattern
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import type { HandlerCallback } from 'yinzerflow';
|
|
145
|
+
|
|
146
|
+
// Define custom types for your API
|
|
147
|
+
interface UserBody {
|
|
148
|
+
name: string;
|
|
149
|
+
email: string;
|
|
150
|
+
age: number;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
interface UserResponse {
|
|
154
|
+
id: string;
|
|
155
|
+
name: string;
|
|
156
|
+
email: string;
|
|
157
|
+
createdAt: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
interface UserQuery {
|
|
161
|
+
include_profile?: string;
|
|
162
|
+
limit?: string;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
interface UserParams {
|
|
166
|
+
id: string;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Typed handler with custom body, response, query, and params
|
|
170
|
+
const createUserHandler: HandlerCallback<{
|
|
171
|
+
body: UserBody;
|
|
172
|
+
response: UserResponse;
|
|
173
|
+
query: UserQuery;
|
|
174
|
+
params: UserParams;
|
|
175
|
+
}> = (ctx) => {
|
|
176
|
+
// ctx.request.body is typed as UserBody
|
|
177
|
+
const userData = ctx.request.body;
|
|
178
|
+
|
|
179
|
+
// ctx.request.query is typed as UserQuery
|
|
180
|
+
const includeProfile = ctx.request.query.include_profile;
|
|
181
|
+
|
|
182
|
+
// ctx.request.params is typed as UserParams
|
|
183
|
+
const userId = ctx.request.params.id;
|
|
184
|
+
|
|
185
|
+
// Return type is typed as UserResponse
|
|
186
|
+
return {
|
|
187
|
+
id: 'user-123',
|
|
188
|
+
name: userData.name,
|
|
189
|
+
email: userData.email,
|
|
190
|
+
createdAt: new Date().toISOString()
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
app.post('/api/users/:id', createUserHandler);
|
|
195
|
+
```
|
|
@@ -40,44 +40,11 @@ These limits are built into the framework and cannot be disabled, ensuring consi
|
|
|
40
40
|
|
|
41
41
|
### Basic Example
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
import { YinzerFlow } from 'yinzerflow';
|
|
45
|
-
|
|
46
|
-
const app = new YinzerFlow({ port: 3000 });
|
|
47
|
-
|
|
48
|
-
app.post('/api/users/:id', ({ request }) => {
|
|
49
|
-
// Access route parameters
|
|
50
|
-
const userId = request.params.id;
|
|
51
|
-
|
|
52
|
-
// Access query parameters
|
|
53
|
-
const includeProfile = request.query.include_profile;
|
|
54
|
-
|
|
55
|
-
// Access headers
|
|
56
|
-
const contentType = request.headers['content-type'];
|
|
57
|
-
const authorization = request.headers['authorization'];
|
|
58
|
-
|
|
59
|
-
// Access request body
|
|
60
|
-
const userData = request.body;
|
|
61
|
-
|
|
62
|
-
// Access raw body for manual parsing when needed
|
|
63
|
-
const rawBody = request.rawBody;
|
|
64
|
-
|
|
65
|
-
const clientIp = request.ipAddress
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
message: 'Request processed successfully',
|
|
69
|
-
userId,
|
|
70
|
-
includeProfile,
|
|
71
|
-
contentType,
|
|
72
|
-
hasAuth: !!authorization,
|
|
73
|
-
receivedData: userData
|
|
74
|
-
};
|
|
75
|
-
});
|
|
76
|
-
```
|
|
43
|
+
See [Request Access Pattern](./examples.md#request-access-pattern) for a complete example showing how to access request properties.
|
|
77
44
|
|
|
78
45
|
### Body Parsing Example
|
|
79
46
|
|
|
80
|
-
YinzerFlow automatically parses request bodies (JSON, file uploads, forms) with built-in security protections. Parsed data is available on `request.body` - see [Body Parsing Documentation](
|
|
47
|
+
YinzerFlow automatically parses request bodies (JSON, file uploads, forms) with built-in security protections. Parsed data is available on `request.body` - see [Body Parsing Documentation](../security/body-parsing.md) for detailed configuration options, examples, and security considerations.
|
|
81
48
|
|
|
82
49
|
```typescript
|
|
83
50
|
app.post('/api/users', ({ request, response }) => {
|
|
@@ -155,7 +122,7 @@ YinzerFlow automatically handles request parsing errors and provides clear error
|
|
|
155
122
|
- `Header value too long: maximum 8192 characters allowed`
|
|
156
123
|
- `Header value contains invalid control characters`
|
|
157
124
|
|
|
158
|
-
**Body parsing errors:** See [Body Parsing Documentation](
|
|
125
|
+
**Body parsing errors:** See [Body Parsing Documentation](../security/body-parsing.md) for detailed error handling
|
|
159
126
|
|
|
160
127
|
These errors automatically result in appropriate HTTP status codes (400 Bad Request) and prevent malformed requests from reaching your application handlers.
|
|
161
128
|
|
|
@@ -30,46 +30,7 @@ YinzerFlow's response handling includes built-in security protections that are a
|
|
|
30
30
|
|
|
31
31
|
## Basic Example
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
import { YinzerFlow } from 'yinzerflow';
|
|
35
|
-
|
|
36
|
-
const app = new YinzerFlow({ port: 3000 });
|
|
37
|
-
|
|
38
|
-
app.get('/api/users/:id', ({ request, response }) => {
|
|
39
|
-
const userId = request.params.id;
|
|
40
|
-
|
|
41
|
-
// Set successful status code
|
|
42
|
-
response.setStatusCode(200);
|
|
43
|
-
|
|
44
|
-
// Add custom headers
|
|
45
|
-
response.addHeaders({
|
|
46
|
-
'X-User-ID': userId,
|
|
47
|
-
'Cache-Control': 'max-age=3600',
|
|
48
|
-
'X-API-Version': '1.0'
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
// Return JSON response body (Content-Type automatically set)
|
|
52
|
-
return {
|
|
53
|
-
id: userId,
|
|
54
|
-
name: 'John Doe',
|
|
55
|
-
email: 'john@example.com',
|
|
56
|
-
timestamp: new Date().toISOString()
|
|
57
|
-
};
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
app.post('/api/files', ({ request, response }) => {
|
|
61
|
-
// Set created status
|
|
62
|
-
response.setStatusCode(201);
|
|
63
|
-
|
|
64
|
-
// Add location header for created resource
|
|
65
|
-
response.addHeaders({
|
|
66
|
-
'Location': '/api/files/12345',
|
|
67
|
-
'X-Upload-Size': request.headers['content-length'] || '0'
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
return { fileId: '12345', status: 'uploaded' };
|
|
71
|
-
});
|
|
72
|
-
```
|
|
33
|
+
See [Response Control Pattern](./examples.md#response-control-pattern) for a complete example showing how to control the response.
|
|
73
34
|
|
|
74
35
|
## Response Body Handling
|
|
75
36
|
|