create-aziz-app 2.0.11 → 2.0.13
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/package.json +3 -2
- package/template/README.md +26 -28
- package/template/api/hello.js +19 -7
- package/template/api/users.js +133 -40
- package/template/app.html +21 -25
- package/template/components/Navbar.jsx +7 -6
- package/template/docs/API-ROUTES.md +538 -0
- package/template/docs/NEXTJS-COMPARISON.md +473 -0
- package/template/modules/api.js +20 -124
- package/template/modules/state.js +225 -31
- package/template/pages/HooksDemo.jsx +194 -0
- package/template/scripts/dev-server.js +170 -1
- package/template/scripts/generate-imports.js +0 -10
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
# Next.js Comparison
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Aziz App is a **lightweight Next.js clone** with all the core benefits but zero dependencies and minimal complexity.
|
|
6
|
+
|
|
7
|
+
## Feature Comparison
|
|
8
|
+
|
|
9
|
+
| Feature | Aziz App | Next.js | Notes |
|
|
10
|
+
|---------|----------|---------|-------|
|
|
11
|
+
| **React Hooks** | ✅ | ✅ | useState, useEffect, useMemo, useCallback, useRef, useReducer |
|
|
12
|
+
| **File-based Routing** | ✅ | ✅ | pages/ folder → routes |
|
|
13
|
+
| **API Routes** | ✅ | ✅ | api/ folder → endpoints |
|
|
14
|
+
| **Server-side API** | ✅ | ✅ | Real Node.js backend |
|
|
15
|
+
| **Hot Reload** | ✅ | ✅ | WebSocket-based |
|
|
16
|
+
| **Auto Import** | ✅ | ✅ | No manual imports needed |
|
|
17
|
+
| **Zero Config** | ✅ | ❌ | No config files needed |
|
|
18
|
+
| **Bundle Size** | **~50KB** | ~300KB+ | 6x smaller! |
|
|
19
|
+
| **Dependencies** | **0** | 20+ | No node_modules bloat |
|
|
20
|
+
| **Build Time** | **Instant** | Slow | No build step |
|
|
21
|
+
| **SSR** | ❌ | ✅ | Client-side only |
|
|
22
|
+
| **SSG** | ❌ | ✅ | Client-side only |
|
|
23
|
+
| **Image Optimization** | ❌ | ✅ | Use CDN instead |
|
|
24
|
+
| **TypeScript** | ❌ | ✅ | Pure JavaScript |
|
|
25
|
+
| **Middleware** | ❌ | ✅ | Keep it simple |
|
|
26
|
+
|
|
27
|
+
## React Hooks - Identical API
|
|
28
|
+
|
|
29
|
+
### useState
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
// Next.js
|
|
33
|
+
import { useState } from 'react';
|
|
34
|
+
const [count, setCount] = useState(0);
|
|
35
|
+
|
|
36
|
+
// Aziz App - EXACTLY THE SAME!
|
|
37
|
+
const [count, setCount] = useState(0);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### useEffect
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
// Next.js
|
|
44
|
+
import { useEffect } from 'react';
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
console.log('Mounted!');
|
|
47
|
+
return () => console.log('Cleanup!');
|
|
48
|
+
}, [dependency]);
|
|
49
|
+
|
|
50
|
+
// Aziz App - EXACTLY THE SAME!
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
console.log('Mounted!');
|
|
53
|
+
return () => console.log('Cleanup!');
|
|
54
|
+
}, [dependency]);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### useMemo
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
// Next.js
|
|
61
|
+
import { useMemo } from 'react';
|
|
62
|
+
const value = useMemo(() => expensiveCalc(), [dep]);
|
|
63
|
+
|
|
64
|
+
// Aziz App - EXACTLY THE SAME!
|
|
65
|
+
const value = useMemo(() => expensiveCalc(), [dep]);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### useCallback
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
// Next.js
|
|
72
|
+
import { useCallback } from 'react';
|
|
73
|
+
const callback = useCallback(() => doSomething(), [dep]);
|
|
74
|
+
|
|
75
|
+
// Aziz App - EXACTLY THE SAME!
|
|
76
|
+
const callback = useCallback(() => doSomething(), [dep]);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### useRef
|
|
80
|
+
|
|
81
|
+
```javascript
|
|
82
|
+
// Next.js
|
|
83
|
+
import { useRef } from 'react';
|
|
84
|
+
const ref = useRef(null);
|
|
85
|
+
|
|
86
|
+
// Aziz App - EXACTLY THE SAME!
|
|
87
|
+
const ref = useRef(null);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### useReducer
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
// Next.js
|
|
94
|
+
import { useReducer } from 'react';
|
|
95
|
+
const [state, dispatch] = useReducer(reducer, initialState);
|
|
96
|
+
|
|
97
|
+
// Aziz App - EXACTLY THE SAME!
|
|
98
|
+
const [state, dispatch] = useReducer(reducer, initialState);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## API Routes - Identical API
|
|
102
|
+
|
|
103
|
+
### Basic Route
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
// Next.js (pages/api/hello.js)
|
|
107
|
+
export default async function handler(req, res) {
|
|
108
|
+
res.status(200).json({ message: 'Hello' });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Aziz App (api/hello.js) - EXACTLY THE SAME!
|
|
112
|
+
export default async function handler(req, res) {
|
|
113
|
+
res.status(200).json({ message: 'Hello' });
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Request Object
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
// Both Next.js and Aziz App
|
|
121
|
+
{
|
|
122
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
|
|
123
|
+
body: {}, // Parsed JSON
|
|
124
|
+
query: {}, // URL parameters
|
|
125
|
+
headers: {}, // Request headers
|
|
126
|
+
url: string // Full URL
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Response Methods
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
// Both Next.js and Aziz App
|
|
134
|
+
res.status(200) // Set status
|
|
135
|
+
res.json({ data: 'value' }) // Send JSON
|
|
136
|
+
res.send('text') // Send text
|
|
137
|
+
res.setHeader('X-Custom', 'value') // Set header
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### CRUD Example
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
// Works identically in both!
|
|
144
|
+
export default async function handler(req, res) {
|
|
145
|
+
switch (req.method) {
|
|
146
|
+
case 'GET':
|
|
147
|
+
return res.status(200).json({ users: [] });
|
|
148
|
+
case 'POST':
|
|
149
|
+
return res.status(201).json({ created: true });
|
|
150
|
+
case 'PUT':
|
|
151
|
+
return res.status(200).json({ updated: true });
|
|
152
|
+
case 'DELETE':
|
|
153
|
+
return res.status(200).json({ deleted: true });
|
|
154
|
+
default:
|
|
155
|
+
return res.status(405).json({ error: 'Method not allowed' });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## File-based Routing
|
|
161
|
+
|
|
162
|
+
### Pages
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
// Next.js
|
|
166
|
+
pages/
|
|
167
|
+
├── index.js → /
|
|
168
|
+
├── about.js → /about
|
|
169
|
+
└── contact.js → /contact
|
|
170
|
+
|
|
171
|
+
// Aziz App - Same concept!
|
|
172
|
+
pages/
|
|
173
|
+
├── Home.jsx → #/home
|
|
174
|
+
├── About.jsx → #/about
|
|
175
|
+
└── Contact.jsx → #/contact
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Auto-Registration
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
// Next.js - Automatic
|
|
182
|
+
// Just create the file
|
|
183
|
+
|
|
184
|
+
// Aziz App - Automatic too!
|
|
185
|
+
// Just name your function: AboutPage()
|
|
186
|
+
function AboutPage() {
|
|
187
|
+
return <div><h1>About</h1></div>;
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Data Fetching
|
|
192
|
+
|
|
193
|
+
### Client-side Fetching
|
|
194
|
+
|
|
195
|
+
```javascript
|
|
196
|
+
// Next.js
|
|
197
|
+
import { useState, useEffect } from 'react';
|
|
198
|
+
|
|
199
|
+
function Page() {
|
|
200
|
+
const [data, setData] = useState(null);
|
|
201
|
+
|
|
202
|
+
useEffect(() => {
|
|
203
|
+
fetch('/api/users')
|
|
204
|
+
.then(res => res.json())
|
|
205
|
+
.then(setData);
|
|
206
|
+
}, []);
|
|
207
|
+
|
|
208
|
+
return <div>{data?.message}</div>;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Aziz App - EXACTLY THE SAME!
|
|
212
|
+
function Page() {
|
|
213
|
+
const [data, setData] = useState(null);
|
|
214
|
+
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
fetch('/api/users')
|
|
217
|
+
.then(res => res.json())
|
|
218
|
+
.then(setData);
|
|
219
|
+
}, []);
|
|
220
|
+
|
|
221
|
+
return <div>{data?.message}</div>;
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Custom Hook
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
// Both Next.js and Aziz App
|
|
229
|
+
function useFetch(url) {
|
|
230
|
+
const [data, setData] = useState(null);
|
|
231
|
+
const [loading, setLoading] = useState(true);
|
|
232
|
+
|
|
233
|
+
useEffect(() => {
|
|
234
|
+
fetch(url)
|
|
235
|
+
.then(res => res.json())
|
|
236
|
+
.then(data => {
|
|
237
|
+
setData(data);
|
|
238
|
+
setLoading(false);
|
|
239
|
+
});
|
|
240
|
+
}, [url]);
|
|
241
|
+
|
|
242
|
+
return { data, loading };
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## JSX Syntax
|
|
247
|
+
|
|
248
|
+
### Components
|
|
249
|
+
|
|
250
|
+
```jsx
|
|
251
|
+
// Next.js
|
|
252
|
+
export default function Button({ onClick, children }) {
|
|
253
|
+
return <button onClick={onClick}>{children}</button>;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Aziz App - Same JSX!
|
|
257
|
+
function Button({ onClick, children }) {
|
|
258
|
+
return <button onClick={onClick}>{children}</button>;
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Props
|
|
263
|
+
|
|
264
|
+
```jsx
|
|
265
|
+
// Both support the same props
|
|
266
|
+
<div
|
|
267
|
+
class="container"
|
|
268
|
+
style={{ color: 'red' }}
|
|
269
|
+
onClick={() => alert('Hi')}
|
|
270
|
+
>
|
|
271
|
+
Content
|
|
272
|
+
</div>
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Development Experience
|
|
276
|
+
|
|
277
|
+
### Hot Reload
|
|
278
|
+
|
|
279
|
+
```
|
|
280
|
+
// Next.js
|
|
281
|
+
1. Save file
|
|
282
|
+
2. Fast Refresh
|
|
283
|
+
3. State preserved
|
|
284
|
+
|
|
285
|
+
// Aziz App
|
|
286
|
+
1. Save file
|
|
287
|
+
2. WebSocket reload
|
|
288
|
+
3. Full page reload (state reset)
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Error Messages
|
|
292
|
+
|
|
293
|
+
```
|
|
294
|
+
// Next.js
|
|
295
|
+
- Detailed error overlay
|
|
296
|
+
- Stack traces
|
|
297
|
+
- Suggestions
|
|
298
|
+
|
|
299
|
+
// Aziz App
|
|
300
|
+
- Browser console errors
|
|
301
|
+
- Simpler debugging
|
|
302
|
+
- Less magic
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## When to Use Each
|
|
306
|
+
|
|
307
|
+
### Use Aziz App When:
|
|
308
|
+
|
|
309
|
+
✅ Building simple SPAs
|
|
310
|
+
✅ Learning React concepts
|
|
311
|
+
✅ Prototyping quickly
|
|
312
|
+
✅ Want zero dependencies
|
|
313
|
+
✅ Don't need SSR/SSG
|
|
314
|
+
✅ Want instant startup
|
|
315
|
+
✅ Prefer simplicity
|
|
316
|
+
|
|
317
|
+
### Use Next.js When:
|
|
318
|
+
|
|
319
|
+
✅ Need SEO (SSR/SSG)
|
|
320
|
+
✅ Building large apps
|
|
321
|
+
✅ Need image optimization
|
|
322
|
+
✅ Want TypeScript
|
|
323
|
+
✅ Need middleware
|
|
324
|
+
✅ Want edge functions
|
|
325
|
+
✅ Building production apps
|
|
326
|
+
|
|
327
|
+
## Migration Path
|
|
328
|
+
|
|
329
|
+
### From Aziz App to Next.js
|
|
330
|
+
|
|
331
|
+
1. **Pages** - Rename files, adjust exports
|
|
332
|
+
2. **API Routes** - Copy as-is (same API!)
|
|
333
|
+
3. **Hooks** - No changes needed (same API!)
|
|
334
|
+
4. **Components** - Minor adjustments
|
|
335
|
+
5. **Styling** - Add CSS modules if needed
|
|
336
|
+
|
|
337
|
+
### From Next.js to Aziz App
|
|
338
|
+
|
|
339
|
+
1. **Remove SSR/SSG** - Convert to client-side
|
|
340
|
+
2. **Pages** - Adjust file names
|
|
341
|
+
3. **API Routes** - Copy as-is (same API!)
|
|
342
|
+
4. **Hooks** - No changes needed (same API!)
|
|
343
|
+
5. **Remove dependencies** - Delete node_modules
|
|
344
|
+
|
|
345
|
+
## Performance
|
|
346
|
+
|
|
347
|
+
### Bundle Size
|
|
348
|
+
|
|
349
|
+
```
|
|
350
|
+
Next.js Production Build:
|
|
351
|
+
- Framework: ~300KB
|
|
352
|
+
- Runtime: ~100KB
|
|
353
|
+
- Total: ~400KB+
|
|
354
|
+
|
|
355
|
+
Aziz App:
|
|
356
|
+
- Framework: ~50KB
|
|
357
|
+
- Runtime: 0KB (browser native)
|
|
358
|
+
- Total: ~50KB
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Startup Time
|
|
362
|
+
|
|
363
|
+
```
|
|
364
|
+
Next.js:
|
|
365
|
+
- npm install: 2-5 minutes
|
|
366
|
+
- npm run dev: 10-30 seconds
|
|
367
|
+
- First load: 2-5 seconds
|
|
368
|
+
|
|
369
|
+
Aziz App:
|
|
370
|
+
- npm install: 5 seconds
|
|
371
|
+
- npm run dev: <1 second
|
|
372
|
+
- First load: <1 second
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Build Time
|
|
376
|
+
|
|
377
|
+
```
|
|
378
|
+
Next.js:
|
|
379
|
+
- Development: 10-30 seconds
|
|
380
|
+
- Production: 1-5 minutes
|
|
381
|
+
|
|
382
|
+
Aziz App:
|
|
383
|
+
- Development: Instant
|
|
384
|
+
- Production: No build needed
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
## Code Examples
|
|
388
|
+
|
|
389
|
+
### Counter Component
|
|
390
|
+
|
|
391
|
+
```jsx
|
|
392
|
+
// Works identically in both!
|
|
393
|
+
function Counter() {
|
|
394
|
+
const [count, setCount] = useState(0);
|
|
395
|
+
|
|
396
|
+
return (
|
|
397
|
+
<div>
|
|
398
|
+
<p>Count: {count}</p>
|
|
399
|
+
<button onClick={() => setCount(count + 1)}>
|
|
400
|
+
Increment
|
|
401
|
+
</button>
|
|
402
|
+
</div>
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Form with Validation
|
|
408
|
+
|
|
409
|
+
```jsx
|
|
410
|
+
// Works identically in both!
|
|
411
|
+
function ContactForm() {
|
|
412
|
+
const [name, setName] = useState('');
|
|
413
|
+
const [email, setEmail] = useState('');
|
|
414
|
+
const [error, setError] = useState('');
|
|
415
|
+
|
|
416
|
+
const handleSubmit = async (e) => {
|
|
417
|
+
e.preventDefault();
|
|
418
|
+
|
|
419
|
+
if (!name || !email) {
|
|
420
|
+
setError('All fields required');
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const res = await fetch('/api/contact', {
|
|
425
|
+
method: 'POST',
|
|
426
|
+
headers: { 'Content-Type': 'application/json' },
|
|
427
|
+
body: JSON.stringify({ name, email })
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
if (res.ok) {
|
|
431
|
+
alert('Sent!');
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
return (
|
|
436
|
+
<form onSubmit={handleSubmit}>
|
|
437
|
+
<input
|
|
438
|
+
value={name}
|
|
439
|
+
onChange={(e) => setName(e.target.value)}
|
|
440
|
+
placeholder="Name"
|
|
441
|
+
/>
|
|
442
|
+
<input
|
|
443
|
+
value={email}
|
|
444
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
445
|
+
placeholder="Email"
|
|
446
|
+
/>
|
|
447
|
+
{error && <p>{error}</p>}
|
|
448
|
+
<button type="submit">Submit</button>
|
|
449
|
+
</form>
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
## Summary
|
|
455
|
+
|
|
456
|
+
Aziz App is a **lightweight Next.js clone** that provides:
|
|
457
|
+
|
|
458
|
+
- ✅ **Same React Hooks API** - useState, useEffect, etc.
|
|
459
|
+
- ✅ **Same API Routes** - Identical req/res objects
|
|
460
|
+
- ✅ **Same JSX Syntax** - Write components the same way
|
|
461
|
+
- ✅ **Same Development Flow** - File-based routing, hot reload
|
|
462
|
+
- ✅ **Zero Dependencies** - No node_modules bloat
|
|
463
|
+
- ✅ **Instant Startup** - No build step
|
|
464
|
+
- ✅ **6x Smaller** - Minimal bundle size
|
|
465
|
+
|
|
466
|
+
Perfect for:
|
|
467
|
+
- 🎓 Learning React/Next.js concepts
|
|
468
|
+
- 🚀 Rapid prototyping
|
|
469
|
+
- 📱 Simple SPAs
|
|
470
|
+
- 🎨 Portfolio sites
|
|
471
|
+
- 🧪 Experiments
|
|
472
|
+
|
|
473
|
+
**It's Next.js, but lightweight!** 🎉
|
package/template/modules/api.js
CHANGED
|
@@ -1,96 +1,12 @@
|
|
|
1
1
|
/** @jsx h */
|
|
2
2
|
|
|
3
3
|
// ============================================================
|
|
4
|
-
// API
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
// API CLIENT
|
|
5
|
+
// Fetch-like API for making requests to backend API routes
|
|
6
|
+
// API routes run on the SERVER (Node.js), not in the browser
|
|
7
7
|
// ============================================================
|
|
8
8
|
|
|
9
|
-
// API
|
|
10
|
-
const apiRoutes = {};
|
|
11
|
-
|
|
12
|
-
// Register an API route
|
|
13
|
-
function registerApiRoute(path, handler) {
|
|
14
|
-
apiRoutes[path] = handler;
|
|
15
|
-
console.log(`Registered API route: /api/${path}`);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// API request/response helpers
|
|
19
|
-
class ApiRequest {
|
|
20
|
-
constructor(method, body, query, params) {
|
|
21
|
-
this.method = method;
|
|
22
|
-
this.body = body;
|
|
23
|
-
this.query = query;
|
|
24
|
-
this.params = params;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
class ApiResponse {
|
|
29
|
-
constructor() {
|
|
30
|
-
this.statusCode = 200;
|
|
31
|
-
this.headers = {};
|
|
32
|
-
this.data = null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
status(code) {
|
|
36
|
-
this.statusCode = code;
|
|
37
|
-
return this;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
json(data) {
|
|
41
|
-
this.data = data;
|
|
42
|
-
this.headers['Content-Type'] = 'application/json';
|
|
43
|
-
return this;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
send(data) {
|
|
47
|
-
this.data = data;
|
|
48
|
-
return this;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Execute an API route
|
|
53
|
-
async function callApi(path, options = {}) {
|
|
54
|
-
const {
|
|
55
|
-
method = 'GET',
|
|
56
|
-
body = null,
|
|
57
|
-
query = {},
|
|
58
|
-
params = {}
|
|
59
|
-
} = options;
|
|
60
|
-
|
|
61
|
-
// Find the route handler
|
|
62
|
-
const handler = apiRoutes[path];
|
|
63
|
-
|
|
64
|
-
if (!handler) {
|
|
65
|
-
return {
|
|
66
|
-
status: 404,
|
|
67
|
-
data: { error: 'API route not found' }
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Create request and response objects
|
|
72
|
-
const req = new ApiRequest(method, body, query, params);
|
|
73
|
-
const res = new ApiResponse();
|
|
74
|
-
|
|
75
|
-
try {
|
|
76
|
-
// Call the handler
|
|
77
|
-
await handler(req, res);
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
status: res.statusCode,
|
|
81
|
-
data: res.data,
|
|
82
|
-
headers: res.headers
|
|
83
|
-
};
|
|
84
|
-
} catch (error) {
|
|
85
|
-
console.error(`API Error on ${path}:`, error);
|
|
86
|
-
return {
|
|
87
|
-
status: 500,
|
|
88
|
-
data: { error: error.message }
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Fetch-like API for making requests
|
|
9
|
+
// Fetch-like API for making requests to backend
|
|
94
10
|
async function fetchApi(path, options = {}) {
|
|
95
11
|
const {
|
|
96
12
|
method = 'GET',
|
|
@@ -98,51 +14,31 @@ async function fetchApi(path, options = {}) {
|
|
|
98
14
|
headers = {}
|
|
99
15
|
} = options;
|
|
100
16
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const [key, value] = param.split('=');
|
|
107
|
-
query[decodeURIComponent(key)] = decodeURIComponent(value);
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Parse body if it's JSON
|
|
112
|
-
let parsedBody = body;
|
|
113
|
-
if (body && typeof body === 'string') {
|
|
114
|
-
try {
|
|
115
|
-
parsedBody = JSON.parse(body);
|
|
116
|
-
} catch (e) {
|
|
117
|
-
parsedBody = body;
|
|
17
|
+
const fetchOptions = {
|
|
18
|
+
method,
|
|
19
|
+
headers: {
|
|
20
|
+
'Content-Type': 'application/json',
|
|
21
|
+
...headers
|
|
118
22
|
}
|
|
119
|
-
}
|
|
23
|
+
};
|
|
120
24
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
query
|
|
125
|
-
});
|
|
25
|
+
if (body) {
|
|
26
|
+
fetchOptions.body = typeof body === 'string' ? body : JSON.stringify(body);
|
|
27
|
+
}
|
|
126
28
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
text: async () => JSON.stringify(result.data)
|
|
135
|
-
};
|
|
29
|
+
try {
|
|
30
|
+
const response = await fetch(path, fetchOptions);
|
|
31
|
+
return response;
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error('API Error:', error);
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
136
36
|
}
|
|
137
37
|
|
|
138
38
|
// Export to global scope
|
|
139
|
-
window.registerApiRoute = registerApiRoute;
|
|
140
39
|
window.fetchApi = fetchApi;
|
|
141
|
-
window.callApi = callApi;
|
|
142
40
|
|
|
143
41
|
// Also make available without window prefix
|
|
144
42
|
if (typeof globalThis !== 'undefined') {
|
|
145
|
-
globalThis.registerApiRoute = registerApiRoute;
|
|
146
43
|
globalThis.fetchApi = fetchApi;
|
|
147
|
-
globalThis.callApi = callApi;
|
|
148
44
|
}
|