qhttpx 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,495 @@
1
+ # QHttpX Migration Guide: 1.9.4 → 2.0.1
2
+
3
+ **Updated:** January 21, 2026
4
+ **Status:** Complete Breaking Changes Guide
5
+
6
+ ---
7
+
8
+ ## Overview
9
+
10
+ QHttpX 2.0 introduces significant improvements in performance, security, and API consistency. While there are breaking changes, migration is straightforward (30 minutes for most projects).
11
+
12
+ ## Quick Summary of Changes
13
+
14
+ | Aspect | 1.9.4 | 2.0+ | Migration |
15
+ |--------|-------|------|-----------|
16
+ | Response handling | `return {}` (implicit) | `json({}, 200)` (explicit) | Update all route handlers |
17
+ | Server startup | `app.listen(port, cb)` | `app.start(port)` (Promise) | Change callback style |
18
+ | Rate limiting | `app.rateLimit('strict')` | `app.security({rateLimit: {...}})` | Update config |
19
+ | Error handling | `throw app.error(401)` | Throw `HttpError` class | Update error creation |
20
+ | Middleware | Basic approach | Lifecycle hooks available | Optional improvements |
21
+
22
+ ---
23
+
24
+ ## Step-by-Step Migration
25
+
26
+ ### Step 1: Update Response Handling
27
+
28
+ **1.9.4 (Implicit JSON):**
29
+ ```typescript
30
+ app.get('/users', async ({ json }) => {
31
+ const users = await getUsers();
32
+ return { users }; // ❌ Implicit - status always 200
33
+ });
34
+
35
+ app.post('/users', async ({ body }) => {
36
+ const user = await createUser(body);
37
+ return { user, created: true }; // ❌ Always returns 200
38
+ });
39
+ ```
40
+
41
+ **2.0.1 (Explicit JSON):**
42
+ ```typescript
43
+ app.get('/users', async ({ json }) => {
44
+ const users = await getUsers();
45
+ json({ users }, 200); // ✅ Explicit status
46
+ });
47
+
48
+ app.post('/users', async ({ body, json }) => {
49
+ const user = await createUser(body);
50
+ json({ user, created: true }, 201); // ✅ 201 Created
51
+ });
52
+ ```
53
+
54
+ **Migration Command:** Search & Replace (with caution):
55
+ ```
56
+ Find: return \({ (.*) }\);
57
+ Replace: json({ $1 }, 200);
58
+ ```
59
+
60
+ **Then manually verify status codes** - change 201, 400, 404, 401 as needed.
61
+
62
+ ---
63
+
64
+ ### Step 2: Update Server Startup
65
+
66
+ **1.9.4 (Callback-based):**
67
+ ```typescript
68
+ app.listen(3000, () => {
69
+ console.log('Server running on http://localhost:3000');
70
+ });
71
+ ```
72
+
73
+ **2.0.1 (Promise-based):**
74
+ ```typescript
75
+ // Option A: Using async/await
76
+ async function start() {
77
+ await app.start(3000);
78
+ console.log('Server running on http://localhost:3000');
79
+ }
80
+ start().catch(console.error);
81
+
82
+ // Option B: Using .then()
83
+ app.start(3000).then(() => {
84
+ console.log('Server running on http://localhost:3000');
85
+ }).catch(console.error);
86
+ ```
87
+
88
+ **Migration Checklist:**
89
+ - [ ] Remove callback from `app.listen()`
90
+ - [ ] Change to `app.start()`
91
+ - [ ] Wrap in async function or use `.then()`
92
+ - [ ] Add error handling with `.catch()`
93
+
94
+ ---
95
+
96
+ ### Step 3: Update Rate Limiting Configuration
97
+
98
+ **1.9.4:**
99
+ ```typescript
100
+ // String-based configuration
101
+ app.rateLimit('strict'); // Pre-defined
102
+ app.rateLimit('moderate'); // Limited customization
103
+ app.rateLimit('permissive');
104
+ ```
105
+
106
+ **2.0.1:**
107
+ ```typescript
108
+ // Granular configuration
109
+ app.security({
110
+ rateLimit: {
111
+ windowMs: 15 * 60 * 1000, // 15 minutes
112
+ max: 100, // 100 requests
113
+ standardHeaders: true, // Return RateLimit-* headers
114
+ legacyHeaders: false, // Disable X-RateLimit-* headers
115
+ trustProxy: true, // Trust X-Forwarded-For
116
+ keyGenerator: (ctx) => ctx.ip, // Custom key (IP by default)
117
+ handler: ({ json }) => json({ error: 'Rate limited' }, 429),
118
+ skip: (ctx) => ctx.ip === '127.0.0.1', // Skip localhost
119
+ }
120
+ });
121
+ ```
122
+
123
+ **Common Presets:**
124
+ ```typescript
125
+ // Strict (like 1.9.4)
126
+ app.security({
127
+ rateLimit: {
128
+ windowMs: 1 * 60 * 1000, // 1 minute
129
+ max: 10, // 10 requests
130
+ }
131
+ });
132
+
133
+ // Moderate
134
+ app.security({
135
+ rateLimit: {
136
+ windowMs: 15 * 60 * 1000, // 15 minutes
137
+ max: 100, // 100 requests
138
+ }
139
+ });
140
+
141
+ // Permissive
142
+ app.security({
143
+ rateLimit: {
144
+ windowMs: 60 * 60 * 1000, // 1 hour
145
+ max: 1000, // 1000 requests
146
+ }
147
+ });
148
+ ```
149
+
150
+ ---
151
+
152
+ ### Step 4: Update Error Handling
153
+
154
+ **1.9.4:**
155
+ ```typescript
156
+ // ❌ This no longer exists
157
+ throw app.error(401, 'Unauthorized');
158
+ throw app.error(404, 'Not found');
159
+ ```
160
+
161
+ **2.0.1 - Option A (Using HttpError):**
162
+ ```typescript
163
+ import { HttpError } from 'qhttpx';
164
+
165
+ app.post('/protected', ({ json, httpError }) => {
166
+ if (!isAuthorized) {
167
+ throw httpError(401, 'Unauthorized', { code: 'AUTH_REQUIRED' });
168
+ }
169
+ json({ success: true }, 200);
170
+ });
171
+ ```
172
+
173
+ **2.0.1 - Option B (Direct JSON response):**
174
+ ```typescript
175
+ app.post('/protected', ({ json }) => {
176
+ if (!isAuthorized) {
177
+ json({ error: 'Unauthorized', code: 'AUTH_REQUIRED' }, 401);
178
+ return; // Important: return to prevent further execution
179
+ }
180
+ json({ success: true }, 200);
181
+ });
182
+ ```
183
+
184
+ **2.0.1 - Option C (Using json() with error):**
185
+ ```typescript
186
+ app.post('/protected', async ({ json }) => {
187
+ try {
188
+ const result = await protectedOperation();
189
+ json({ data: result }, 200);
190
+ } catch (error) {
191
+ if (error.code === 'AUTH_REQUIRED') {
192
+ json({ error: error.message, code: error.code }, 401);
193
+ } else {
194
+ json({ error: 'Internal server error' }, 500);
195
+ }
196
+ }
197
+ });
198
+ ```
199
+
200
+ **Migration Path:**
201
+ 1. Replace `app.error()` calls with `httpError()`
202
+ 2. Or use `json({ error: msg }, status); return;`
203
+ 3. Preferred: Use try-catch with explicit json responses
204
+
205
+ ---
206
+
207
+ ### Step 5: Update Configuration
208
+
209
+ **1.9.4:**
210
+ ```typescript
211
+ app
212
+ .rateLimit('strict')
213
+ .production()
214
+ .compression()
215
+ .cors();
216
+ ```
217
+
218
+ **2.0.1:**
219
+ ```typescript
220
+ app
221
+ .fusion(true) // Enable Request Fusion
222
+ .metrics(true) // Enable metrics collection
223
+ .production() // Enable production optimizations
224
+ .security({
225
+ cors: {
226
+ origin: '*',
227
+ methods: ['GET', 'POST', 'PUT', 'DELETE'],
228
+ credentials: false,
229
+ },
230
+ rateLimit: {
231
+ windowMs: 15 * 60 * 1000,
232
+ max: 100,
233
+ },
234
+ headers: true, // Enable security headers
235
+ });
236
+ ```
237
+
238
+ ---
239
+
240
+ ### Step 6: Update Middleware (Optional)
241
+
242
+ **1.9.4:**
243
+ ```typescript
244
+ app.use(async ({ req, next }) => {
245
+ console.log(`${req.method} ${req.url}`);
246
+ if (next) await next();
247
+ });
248
+ ```
249
+
250
+ **2.0.1 - Same approach still works:**
251
+ ```typescript
252
+ app.use(async ({ req, next }) => {
253
+ console.log(`${req.method} ${req.url}`);
254
+ if (next) await next();
255
+ });
256
+ ```
257
+
258
+ **2.0.1 - New lifecycle hooks (optional):**
259
+ ```typescript
260
+ // Before handler
261
+ app.onRequest(({ req }) => {
262
+ console.log(`→ ${req.method} ${req.url}`);
263
+ });
264
+
265
+ // After handler
266
+ app.onResponse(({ req, status }) => {
267
+ console.log(`← ${req.method} ${req.url} ${status}`);
268
+ });
269
+
270
+ // On error
271
+ app.onError(({ req, error }) => {
272
+ console.error(`❌ ${req.method} ${req.url}:`, error.message);
273
+ });
274
+ ```
275
+
276
+ ---
277
+
278
+ ## Full Migration Example
279
+
280
+ ### Before (1.9.4):
281
+
282
+ ```typescript
283
+ import { app } from 'qhttpx';
284
+
285
+ app.rateLimit('strict').production();
286
+
287
+ app.get('/', ({ json }) => {
288
+ return { status: 'ok' };
289
+ });
290
+
291
+ app.post('/users', async ({ body, json }) => {
292
+ const { email, name } = body as any;
293
+
294
+ if (!email) {
295
+ throw app.error(400, 'Email required');
296
+ }
297
+
298
+ const user = await createUser(email, name);
299
+ return { user };
300
+ });
301
+
302
+ app.onError(({ error, json }) => {
303
+ json({ error: error.message }, 500);
304
+ });
305
+
306
+ app.listen(3000, () => {
307
+ console.log('Server running');
308
+ });
309
+ ```
310
+
311
+ ### After (2.0.1):
312
+
313
+ ```typescript
314
+ import { app, HttpError } from 'qhttpx';
315
+
316
+ app
317
+ .production()
318
+ .security({
319
+ rateLimit: {
320
+ windowMs: 1 * 60 * 1000,
321
+ max: 10,
322
+ }
323
+ });
324
+
325
+ app.get('/', ({ json }) => {
326
+ json({ status: 'ok' }, 200);
327
+ });
328
+
329
+ app.post('/users', async ({ body, json, httpError }) => {
330
+ const { email, name } = body as any;
331
+
332
+ if (!email) {
333
+ throw httpError(400, 'Email required');
334
+ }
335
+
336
+ const user = await createUser(email, name);
337
+ json({ user }, 201); // 201 Created
338
+ });
339
+
340
+ app.onError(({ error, json }) => {
341
+ const status = error instanceof HttpError ? error.status : 500;
342
+ json({ error: error.message }, status);
343
+ });
344
+
345
+ app.start(3000).then(() => {
346
+ console.log('Server running');
347
+ }).catch(console.error);
348
+ ```
349
+
350
+ ---
351
+
352
+ ## Automated Migration Script
353
+
354
+ **Create a script to help with common replacements:**
355
+
356
+ ```bash
357
+ # Find all 'return {' statements in routes
358
+ grep -r "return {" src/routes/ | wc -l
359
+
360
+ # Find all 'app.error' calls
361
+ grep -r "app\.error" src/ | wc -l
362
+
363
+ # Find all 'app.listen' calls
364
+ grep -r "app\.listen" src/ | wc -l
365
+ ```
366
+
367
+ ---
368
+
369
+ ## Common Pitfalls
370
+
371
+ ### ❌ Pitfall 1: Forgetting to call json()
372
+
373
+ ```typescript
374
+ // WRONG - Returns nothing
375
+ app.get('/users', ({ json }) => {
376
+ const users = getUsers();
377
+ // Missing: json(...)
378
+ });
379
+
380
+ // RIGHT
381
+ app.get('/users', ({ json }) => {
382
+ const users = getUsers();
383
+ json({ users }, 200);
384
+ });
385
+ ```
386
+
387
+ ### ❌ Pitfall 2: Not returning after json() on error paths
388
+
389
+ ```typescript
390
+ // WRONG - Continues execution
391
+ app.post('/users', ({ json }) => {
392
+ if (!isValid) {
393
+ json({ error: 'Invalid' }, 400);
394
+ // Missing: return
395
+ }
396
+ json({ success: true }, 200); // This still executes!
397
+ });
398
+
399
+ // RIGHT
400
+ app.post('/users', ({ json }) => {
401
+ if (!isValid) {
402
+ json({ error: 'Invalid' }, 400);
403
+ return; // Stop execution
404
+ }
405
+ json({ success: true }, 200);
406
+ });
407
+ ```
408
+
409
+ ### ❌ Pitfall 3: Using old error creation
410
+
411
+ ```typescript
412
+ // WRONG - app.error() doesn't exist
413
+ throw app.error(404, 'Not found');
414
+
415
+ // RIGHT
416
+ throw httpError(404, 'Not found');
417
+ ```
418
+
419
+ ### ❌ Pitfall 4: Async/await with app.start()
420
+
421
+ ```typescript
422
+ // WRONG - Not waiting for start
423
+ app.start(3000);
424
+ console.log('Running'); // May print before server is ready!
425
+
426
+ // RIGHT
427
+ async function start() {
428
+ await app.start(3000);
429
+ console.log('Running');
430
+ }
431
+ start();
432
+ ```
433
+
434
+ ---
435
+
436
+ ## Testing After Migration
437
+
438
+ ### Checklist:
439
+
440
+ - [ ] All routes return via `json()` or `send()`
441
+ - [ ] All error cases return proper status codes
442
+ - [ ] No more `app.error()` calls
443
+ - [ ] `app.listen()` changed to `app.start()`
444
+ - [ ] Error handler updated for new error types
445
+ - [ ] Rate limiting configured explicitly
446
+ - [ ] TypeScript compiles without errors
447
+ - [ ] All endpoints tested in Postman/curl
448
+
449
+ ### Test Command:
450
+
451
+ ```bash
452
+ # Compile TypeScript
453
+ npm run build
454
+
455
+ # Start server
456
+ npm run start
457
+
458
+ # Test endpoints
459
+ curl http://localhost:3000/
460
+ curl -X POST http://localhost:3000/users -H "Content-Type: application/json" -d '{"email":"test@example.com","name":"Test"}'
461
+ ```
462
+
463
+ ---
464
+
465
+ ## Getting Help
466
+
467
+ 1. **Check examples:** `QHttpX/examples/` directory
468
+ 2. **Read API docs:** Check docs/ folder (v2.0.1+)
469
+ 3. **GitHub Issues:** Report problems
470
+ 4. **Community:** Ask on GitHub Discussions
471
+
472
+ ---
473
+
474
+ ## Version Support
475
+
476
+ - **v1.9.4:** No longer supported (use 2.0+)
477
+ - **v2.0.0+:** Current stable version
478
+ - **v2.1.0+:** Recommended (better docs, more features)
479
+
480
+ **Timeline:**
481
+ - v2.0.1: January 2026
482
+ - v2.1.0: February 2026 (docs + features)
483
+ - v3.0.0: Q2 2026 (stable API)
484
+
485
+ ---
486
+
487
+ ## Feedback
488
+
489
+ Found issues with this guide? Please:
490
+ 1. Create a GitHub issue with "[DOCS]" prefix
491
+ 2. Include your version number
492
+ 3. Describe the unclear part
493
+ 4. Suggest improvement
494
+
495
+ Thank you for helping make QHttpX better! 🙏