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,230 +0,0 @@
1
- # Context Object
2
-
3
- The Context object is the central interface for all YinzerFlow route handlers and hooks. It provides access to the request data, response controls, and maintains the complete request lifecycle state.
4
-
5
- ## Configuration
6
-
7
- Context objects are automatically created and provided to all handlers - no configuration required:
8
-
9
- ```typescript
10
- import { YinzerFlow } from 'yinzerflow';
11
-
12
- const app = new YinzerFlow({ port: 3000 });
13
-
14
- // Context is automatically provided to all handlers
15
- app.get('/api/data', (ctx) => {
16
- // Access request data
17
- const { path, method, headers } = ctx.request;
18
-
19
- // Control response
20
- ctx.response.setStatusCode(200);
21
-
22
- return { message: 'Success' };
23
- });
24
- ```
25
-
26
- ## Examples
27
-
28
- ### Basic Example
29
-
30
- See [Basic Handler Pattern](./examples.md#basic-handler-pattern) for a complete example showing how to use the context object.
31
-
32
- For detailed information about request properties and methods, see [Request Object Documentation](./request.md).
33
- For detailed information about response methods and capabilities, see [Response Object Documentation](./response.md).
34
-
35
- ## Common Use Cases
36
-
37
- - **Request Data Access**: Extract headers, body, query parameters, and route parameters for processing
38
- - **Response Control**: Set status codes, add headers, and control response formatting
39
- - **Authentication**: Access authorization headers and client IP for security validation
40
- - **Content Negotiation**: Handle Accept headers and Content-Type for proper response formatting
41
- - **Error Handling**: Provide context to error handlers for detailed error responses
42
- - **Logging and Monitoring**: Access request metadata for logging and analytics
43
-
44
- ## Context Structure
45
-
46
- The Context object contains two main properties:
47
-
48
- ### Request Object (`ctx.request`)
49
- Contains all incoming request data including headers, body, query parameters, route parameters, and metadata. See [Request Object Documentation](./request.md) for detailed information about request properties and methods.
50
-
51
- ### Response Object (`ctx.response`)
52
- Provides methods to control the HTTP response including status codes, headers, and response formatting. See [Response Object Documentation](./response.md) for detailed information about response methods and capabilities.
53
-
54
- ## Context State
55
-
56
- The Context object provides a powerful state system that allows you to store and share custom data throughout the request lifecycle. This is perfect for authentication, middleware data, request-scoped variables, and custom context information.
57
-
58
- ### What is Context State?
59
-
60
- Context state is a request-scoped object (`ctx.state`) that persists data throughout the entire request lifecycle. Unlike global variables, state is isolated to each individual request and automatically garbage collected when the request completes.
61
-
62
- ### Basic Usage
63
-
64
- ```typescript
65
- app.get('/api/users', async (ctx) => {
66
- // Store custom data in state
67
- ctx.state.user = { id: 1, name: 'John' };
68
- ctx.state.requestId = generateRequestId();
69
- ctx.state.timestamp = Date.now();
70
-
71
- // Access the data later
72
- console.log(ctx.state.user.name); // "John"
73
- console.log(ctx.state.requestId); // "req-123"
74
- console.log(ctx.state.timestamp); // 1703123456789
75
-
76
- return { users: ['John', 'Jane'] };
77
- });
78
- ```
79
-
80
- ### Advanced Usage with Type Safety
81
-
82
- For full type safety, you can extend the context with custom state types:
83
-
84
- ```typescript
85
- // Define your custom state interface
86
- interface AuthContext extends InternalHandlerCallbackGenerics {
87
- state: {
88
- user: User;
89
- permissions: string[];
90
- requestId: string;
91
- session: Session;
92
- };
93
- }
94
-
95
- // Use it in your route handler
96
- const authHandler: HandlerCallback<AuthContext> = async (ctx) => {
97
- // Fully type-safe access to state
98
- console.log(ctx.state.user.name); // Type: string
99
- console.log(ctx.state.permissions[0]); // Type: string
100
- console.log(ctx.state.session.expiresAt); // Type: Date
101
-
102
- // No type assertions needed!
103
- const user = ctx.state.user; // Type: User
104
- const permissions = ctx.state.permissions; // Type: string[]
105
-
106
- return { message: 'Authenticated', user };
107
- };
108
- ```
109
-
110
- ### State Lifecycle
111
-
112
- State data follows the request lifecycle:
113
-
114
- 1. **Request Start**: State object is created as empty object
115
- 2. **Global Hooks**: `beforeAll` hooks can populate state
116
- 3. **Route Hooks**: `beforeHooks` can access and modify state
117
- 4. **Route Handler**: Your handler can access and modify state
118
- 5. **Route Hooks**: `afterHooks` can access state and modify response
119
- 6. **Global Hooks**: `afterAll` hooks can access state and modify response
120
- 7. **Request End**: State is automatically garbage collected
121
-
122
- ### State Inheritance in Route Groups
123
-
124
- State can be shared across route groups and inherited by nested routes:
125
-
126
- ```typescript
127
- app.group('/api/v1', (api) => {
128
- // Group-level middleware sets state
129
- api.beforeAll([async (ctx) => {
130
- ctx.state.apiVersion = 'v1';
131
- ctx.state.environment = process.env.NODE_ENV;
132
- }]);
133
-
134
- api.group('/admin', (admin) => {
135
- // Admin-specific middleware
136
- admin.beforeAll([async (ctx) => {
137
- ctx.state.requiresAuth = true;
138
- ctx.state.adminOnly = true;
139
- }]);
140
-
141
- // Routes inherit all state from parent groups
142
- admin.get('/users', async (ctx) => {
143
- console.log(ctx.state.apiVersion); // "v1"
144
- console.log(ctx.state.environment); // "production"
145
- console.log(ctx.state.requiresAuth); // true
146
- console.log(ctx.state.adminOnly); // true
147
-
148
- return { users: ['Admin1', 'Admin2'] };
149
- });
150
- });
151
- });
152
- ```
153
-
154
- ### Common Use Cases
155
-
156
- - **Authentication**: Store user information and permissions
157
- - **Request Tracking**: Generate and store request IDs for logging
158
- - **Middleware Data**: Pass data between middleware and route handlers
159
- - **Session Management**: Store session information and user preferences
160
- - **Rate Limiting**: Track request counts and limits per user/IP
161
- - **Custom Headers**: Store computed headers for later use
162
- - **Validation Results**: Cache validation results to avoid re-validation
163
- - **Database Connections**: Store database connection pools or transactions
164
-
165
- ### Best Practices
166
-
167
- - **Keep state minimal**: Only store data that's actually needed
168
- - **Use descriptive keys**: `ctx.state.user` is better than `ctx.state.u`
169
- - **Validate state access**: Check if properties exist before using them
170
- - **Type your state**: Use TypeScript interfaces for complex state structures
171
- - **Don't store sensitive data**: State is not encrypted or secured
172
- - **Clean up resources**: Release any resources (like DB connections) when done
173
-
174
- ### Security Considerations
175
-
176
- YinzerFlow implements several security measures for context state:
177
-
178
- #### 🛡️ Request Isolation
179
- - **Problem**: State data could leak between requests
180
- - **YinzerFlow Solution**: Each request gets its own isolated state object
181
-
182
- #### 🛡️ Type Safety
183
- - **Problem**: Unchecked state access can lead to runtime errors
184
- - **YinzerFlow Solution**: TypeScript generics provide compile-time type checking
185
-
186
- #### 🛡️ Memory Management
187
- - **Problem**: State data could accumulate and cause memory leaks
188
- - **YinzerFlow Solution**: State is automatically garbage collected after each request
189
-
190
- These security measures ensure YinzerFlow's context state implementation follows security best practices and prevents common attack vectors while maintaining spec compliance.
191
-
192
- ## TypeScript Support
193
-
194
- YinzerFlow provides full TypeScript support for context objects with generic type parameters.
195
-
196
- See [TypeScript Pattern](./examples.md#typescript-pattern) for a complete example showing how to use TypeScript with the context object.
197
-
198
- ## Error Handling Integration
199
-
200
- Context objects are automatically passed to error handlers.
201
-
202
- See [Error Handler Pattern](./examples.md#error-handler-pattern) for a complete example.
203
-
204
- ## Hook Integration
205
-
206
- Context objects are passed to all hooks (before/after hooks).
207
-
208
- See [Hook Pattern](./examples.md#hook-pattern) for a complete example.
209
-
210
- ## Security Considerations
211
-
212
- YinzerFlow implements several security measures for context handling:
213
-
214
- ### 🛡️ Input Validation
215
- - **Problem**: Malicious request data can cause injection attacks or bypass security controls
216
- - **YinzerFlow Solution**: All request properties are automatically validated and sanitized
217
-
218
- ### 🛡️ Type Safety
219
- - **Problem**: Untyped request data can lead to runtime errors and security vulnerabilities
220
- - **YinzerFlow Solution**: Full TypeScript support with generic type parameters ensures type safety
221
-
222
- ### 🛡️ Context Isolation
223
- - **Problem**: Context objects could be modified maliciously to bypass security controls
224
- - **YinzerFlow Solution**: Context objects are isolated and modifications are controlled
225
-
226
- ### 🛡️ Header Security
227
- - **Problem**: Malicious headers can cause parsing errors or security bypasses
228
- - **YinzerFlow Solution**: Header validation and sanitization prevent header-based attacks
229
-
230
- These security measures ensure YinzerFlow's context implementation follows security best practices and prevents common attack vectors while maintaining type safety and developer experience.
@@ -1,444 +0,0 @@
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
- ## Context State Examples
101
-
102
- ### Basic State Usage
103
-
104
- ```typescript
105
- app.get('/api/users', async (ctx) => {
106
- // Store simple data in state
107
- ctx.state.requestId = generateRequestId();
108
- ctx.state.timestamp = Date.now();
109
- ctx.state.clientIp = ctx.request.ipAddress;
110
-
111
- // Access the stored data
112
- console.log(`Request ${ctx.state.requestId} from ${ctx.state.clientIp}`);
113
-
114
- return { users: ['John', 'Jane'] };
115
- });
116
- ```
117
-
118
- ### Authentication State Pattern
119
-
120
- ```typescript
121
- // Authentication middleware
122
- const authMiddleware: HandlerCallback = async (ctx) => {
123
- const token = ctx.request.headers.authorization?.replace('Bearer ', '');
124
-
125
- if (!token) {
126
- throw new Error('Unauthorized');
127
- }
128
-
129
- try {
130
- const user = await validateJWT(token);
131
- const permissions = await getUserPermissions(user.id);
132
-
133
- // Store authentication data in state
134
- ctx.state.user = user;
135
- ctx.state.permissions = permissions;
136
- ctx.state.isAuthenticated = true;
137
- ctx.state.authTimestamp = Date.now();
138
- } catch (error) {
139
- throw new Error('Invalid token');
140
- }
141
- };
142
-
143
- // Protected route using the state
144
- app.get('/api/admin/users', authMiddleware, async (ctx) => {
145
- // Access the authenticated user data
146
- const { user, permissions, isAuthenticated } = ctx.state;
147
-
148
- if (!permissions.includes('admin')) {
149
- throw new Error('Insufficient permissions');
150
- }
151
-
152
- return {
153
- message: 'Admin access granted',
154
- user: { id: user.id, name: user.name },
155
- permissions,
156
- authenticatedAt: ctx.state.authTimestamp
157
- };
158
- });
159
- ```
160
-
161
- ### Typed State Pattern
162
-
163
- ```typescript
164
- // Define your state interface
165
- interface UserContext extends InternalHandlerCallbackGenerics {
166
- state: {
167
- user: User;
168
- permissions: string[];
169
- requestId: string;
170
- session: {
171
- id: string;
172
- expiresAt: Date;
173
- lastActivity: Date;
174
- };
175
- };
176
- }
177
-
178
- // Use typed state in your handler
179
- const userProfileHandler: HandlerCallback<UserContext> = async (ctx) => {
180
- // Fully type-safe access to state
181
- const { user, permissions, session } = ctx.state;
182
-
183
- // No type assertions needed!
184
- if (session.expiresAt < new Date()) {
185
- throw new Error('Session expired');
186
- }
187
-
188
- // Update session activity
189
- ctx.state.session.lastActivity = new Date();
190
-
191
- return {
192
- profile: {
193
- id: user.id,
194
- name: user.name,
195
- email: user.email,
196
- permissions
197
- },
198
- session: {
199
- id: session.id,
200
- expiresAt: session.expiresAt,
201
- lastActivity: ctx.state.session.lastActivity
202
- }
203
- };
204
- };
205
- ```
206
-
207
- ### Middleware Chain State Pattern
208
-
209
- ```typescript
210
- // Rate limiting middleware
211
- const rateLimitMiddleware: HandlerCallback = async (ctx) => {
212
- const clientIp = ctx.request.ipAddress;
213
- const currentCount = await getRequestCount(clientIp);
214
-
215
- if (currentCount > 100) {
216
- throw new Error('Rate limit exceeded');
217
- }
218
-
219
- // Store rate limiting data in state
220
- ctx.state.rateLimit = {
221
- clientIp,
222
- currentCount,
223
- limit: 100,
224
- resetTime: Date.now() + 60000 // 1 minute
225
- };
226
-
227
- await incrementRequestCount(clientIp);
228
- };
229
-
230
- // Logging middleware
231
- const loggingMiddleware: HandlerCallback = async (ctx) => {
232
- // Access state from previous middleware
233
- const rateLimit = ctx.state.rateLimit;
234
-
235
- ctx.state.logData = {
236
- requestId: generateRequestId(),
237
- timestamp: Date.now(),
238
- clientIp: ctx.request.ipAddress,
239
- rateLimitInfo: rateLimit,
240
- userAgent: ctx.request.headers['user-agent']
241
- };
242
-
243
- console.log('Request started:', ctx.state.logData);
244
- };
245
-
246
- // Route using both middlewares
247
- app.get('/api/data', rateLimitMiddleware, loggingMiddleware, async (ctx) => {
248
- // Access state from all previous middleware
249
- const { rateLimit, logData } = ctx.state;
250
-
251
- // Add route-specific data to state
252
- ctx.state.routeData = {
253
- endpoint: '/api/data',
254
- method: 'GET',
255
- processingTime: Date.now() - logData.timestamp
256
- };
257
-
258
- return {
259
- message: 'Data retrieved successfully',
260
- rateLimit: {
261
- remaining: rateLimit.limit - rateLimit.currentCount,
262
- resetTime: rateLimit.resetTime
263
- },
264
- requestInfo: logData,
265
- routeInfo: ctx.state.routeData
266
- };
267
- });
268
- ```
269
-
270
- ### Route Group State Pattern
271
-
272
- ```typescript
273
- app.group('/api/v1', (api) => {
274
- // Global API state
275
- api.beforeAll([async (ctx) => {
276
- ctx.state.apiVersion = 'v1';
277
- ctx.state.environment = process.env.NODE_ENV;
278
- ctx.state.baseUrl = 'https://api.example.com';
279
- }]);
280
-
281
- api.group('/admin', (admin) => {
282
- // Admin-specific state
283
- admin.beforeAll([async (ctx) => {
284
- ctx.state.requiresAuth = true;
285
- ctx.state.adminOnly = true;
286
- ctx.state.auditLog = true;
287
- }]);
288
-
289
- admin.group('/users', (users) => {
290
- // User management state
291
- users.beforeAll([async (ctx) => {
292
- ctx.state.resourceType = 'user';
293
- ctx.state.allowedOperations = ['create', 'read', 'update', 'delete'];
294
- }]);
295
-
296
- // Route inherits all state from parent groups
297
- users.get('/', async (ctx) => {
298
- const {
299
- apiVersion, // "v1"
300
- environment, // "production"
301
- requiresAuth, // true
302
- adminOnly, // true
303
- auditLog, // true
304
- resourceType, // "user"
305
- allowedOperations // ["create", "read", "update", "delete"]
306
- } = ctx.state;
307
-
308
- return {
309
- message: 'User list retrieved',
310
- apiInfo: { version: apiVersion, environment },
311
- security: { requiresAuth, adminOnly, auditLog },
312
- resource: { type: resourceType, operations: allowedOperations }
313
- };
314
- });
315
- });
316
- });
317
- });
318
- ```
319
-
320
- ### State Validation Pattern
321
-
322
- ```typescript
323
- // State validation helper
324
- const validateState = (ctx: Context, requiredKeys: string[]) => {
325
- const missing = requiredKeys.filter(key => !(key in ctx.state));
326
-
327
- if (missing.length > 0) {
328
- throw new Error(`Missing required state: ${missing.join(', ')}`);
329
- }
330
- };
331
-
332
- // Middleware that sets required state
333
- const userContextMiddleware: HandlerCallback = async (ctx) => {
334
- const userId = ctx.request.params.userId;
335
-
336
- if (!userId) {
337
- throw new Error('User ID required');
338
- }
339
-
340
- const user = await getUserById(userId);
341
- if (!user) {
342
- throw new Error('User not found');
343
- }
344
-
345
- // Set required state
346
- ctx.state.user = user;
347
- ctx.state.userId = userId;
348
- ctx.state.userPermissions = await getUserPermissions(userId);
349
- };
350
-
351
- // Route that validates state
352
- app.get('/api/users/:userId/profile', userContextMiddleware, async (ctx) => {
353
- // Validate required state
354
- validateState(ctx, ['user', 'userId', 'userPermissions']);
355
-
356
- // Now we can safely access state
357
- const { user, userPermissions } = ctx.state;
358
-
359
- return {
360
- profile: user,
361
- permissions: userPermissions,
362
- lastAccessed: new Date().toISOString()
363
- };
364
- });
365
- ```
366
-
367
- ## Hook Pattern
368
-
369
- ```typescript
370
- // Before hook
371
- app.beforeAll([(ctx) => {
372
- // Access request context
373
- const authHeader = ctx.request.headers['authorization'];
374
-
375
- // Validate authentication
376
- if (!authHeader) {
377
- throw new Error('Authentication required');
378
- }
379
- }]);
380
-
381
- // After hook
382
- app.afterAll([(ctx) => {
383
- // Access response context
384
- ctx.response.addHeaders({
385
- 'X-Response-Time': Date.now().toString()
386
- });
387
- }]);
388
- ```
389
-
390
- ## TypeScript Pattern
391
-
392
- ```typescript
393
- import type { HandlerCallback } from 'yinzerflow';
394
-
395
- // Define custom types for your API
396
- interface UserBody {
397
- name: string;
398
- email: string;
399
- age: number;
400
- }
401
-
402
- interface UserResponse {
403
- id: string;
404
- name: string;
405
- email: string;
406
- createdAt: string;
407
- }
408
-
409
- interface UserQuery {
410
- include_profile?: string;
411
- limit?: string;
412
- }
413
-
414
- interface UserParams {
415
- id: string;
416
- }
417
-
418
- // Typed handler with custom body, response, query, and params
419
- const createUserHandler: HandlerCallback<{
420
- body: UserBody;
421
- response: UserResponse;
422
- query: UserQuery;
423
- params: UserParams;
424
- }> = (ctx) => {
425
- // ctx.request.body is typed as UserBody
426
- const userData = ctx.request.body;
427
-
428
- // ctx.request.query is typed as UserQuery
429
- const includeProfile = ctx.request.query.include_profile;
430
-
431
- // ctx.request.params is typed as UserParams
432
- const userId = ctx.request.params.id;
433
-
434
- // Return type is typed as UserResponse
435
- return {
436
- id: 'user-123',
437
- name: userData.name,
438
- email: userData.email,
439
- createdAt: new Date().toISOString()
440
- };
441
- };
442
-
443
- app.post('/api/users/:id', createUserHandler);
444
- ```