create-aziz-app 3.1.1 → 3.1.3

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.
Files changed (33) hide show
  1. package/README.md +658 -207
  2. package/bin/create-aziz-app.js +302 -351
  3. package/package.json +1 -1
  4. package/template/README.md +14 -279
  5. package/template/_framework/api.js +219 -219
  6. package/template/_framework/boot.js +33 -33
  7. package/template/_framework/hooks.js +367 -291
  8. package/template/_framework/next-response.js +128 -128
  9. package/template/_framework/route-match.js +145 -145
  10. package/template/_framework/router.js +465 -259
  11. package/template/_framework/runtime.js +264 -264
  12. package/template/app/layout.js +3 -5
  13. package/template/app/page.js +9 -32
  14. package/template/docs/01-getting-started.md +151 -151
  15. package/template/docs/02-project-structure.md +109 -109
  16. package/template/docs/03-pages-and-layouts.md +140 -140
  17. package/template/docs/04-routing.md +120 -120
  18. package/template/docs/05-hooks.md +169 -169
  19. package/template/docs/06-api-routes.md +210 -210
  20. package/template/docs/07-api-client.md +151 -151
  21. package/template/docs/08-styling.md +94 -94
  22. package/template/docs/09-imports-and-modules.md +97 -97
  23. package/template/docs/10-database-and-models.md +154 -154
  24. package/template/docs/11-authentication.md +171 -171
  25. package/template/docs/12-production-and-deployment.md +121 -121
  26. package/template/docs/13-internals.md +179 -179
  27. package/template/docs/README.md +67 -67
  28. package/template/index.html +20 -20
  29. package/template/package.json +16 -16
  30. package/template/scripts/dev-server.js +493 -381
  31. package/template/scripts/lib/api-router.js +525 -525
  32. package/template/scripts/lib/js-transform.js +213 -141
  33. package/template/scripts/lib/module-resolver.js +132 -132
@@ -1,210 +1,210 @@
1
- # 06 · API Routes
2
-
3
- API routes are the **server side** of your app — real Node.js handlers you call over HTTP. They're how you fetch data, handle forms, and run database queries without exposing secrets in the browser.
4
-
5
- ## Where routes live
6
-
7
- Two equivalent layouts, both producing `/api/**` URLs:
8
-
9
- ```
10
- api/users.js → /api/users (classic)
11
- api/users/[id].js → /api/users/:id
12
- app/api/users/route.js → /api/users (Next.js style)
13
- app/api/users/[id]/route.js → /api/users/:id
14
- ```
15
-
16
- - **Classic:** `api/<name>.js` → `/api/<name>`; folders nest: `api/posts/[id]/comments.js`.
17
- - **Next style:** any `route.js` under `app/api/` → the folder path mapped to `/api/...`.
18
-
19
- Both can coexist. Use whichever reads better.
20
-
21
- ## Your first handler
22
-
23
- Export functions named after HTTP methods:
24
-
25
- ```js
26
- // api/hello.js → /api/hello
27
- module.exports = {
28
- GET: async (request) => {
29
- const name = request.query.get('name') || 'world';
30
- return { hello: name };
31
- },
32
-
33
- POST: async (request) => {
34
- const body = await request.json();
35
- return { created: true, ...body };
36
- },
37
- };
38
- ```
39
-
40
- **Return a value — don't manage a raw socket.** The framework turns it into the HTTP response:
41
-
42
- | You return | Response |
43
- |------------|----------|
44
- | `{ ok: true }` / `[]` | `200` + JSON |
45
- | `'some text'` | `200` + `text/plain` |
46
- | `NextResponse.json(data, { status: 400 })` | `400` + your headers |
47
- | `NextResponse.redirect('/login')` | `307` + `Location` |
48
- | `undefined` / `null` | `500` + a helpful error (you forgot to return) |
49
-
50
- ## The `request` object
51
-
52
- ```js
53
- GET: async (request) => {
54
- request.method // 'GET'
55
- request.url // full URL
56
- request.params // { id: '42' } from [id] segments
57
- request.query // URLSearchParams → get('page'), getAll('tag')
58
- request.headers // raw Node headers (lowercase keys)
59
- request.cookies // { token: '…' } parsed from Cookie header
60
- request.rawBody // raw body string
61
- request.body // parsed JSON if content-type was JSON
62
-
63
- await request.json() // parse JSON body (throws 400 on invalid)
64
- await request.text() // raw text
65
- await request.formData()// urlencoded → { field: 'value' }
66
- }
67
- ```
68
-
69
- ### Dynamic and catch-all params
70
-
71
- ```js
72
- // api/users/[id].js
73
- GET: async (request) => request.params.id // /api/users/7 → "7"
74
-
75
- // api/files/[...path].js
76
- GET: async (request) => request.params.path // /api/files/a/b → ['a','b']
77
- ```
78
-
79
- The literal prefix is validated — `api/files/[...path]` won't match `/api/other/x`.
80
-
81
- ### Query strings
82
-
83
- ```js
84
- // GET /api/search?q=aziz&tag=a&tag=b
85
- request.query.get('q') // "aziz"
86
- request.query.getAll('tag') // ["a", "b"]
87
- ```
88
-
89
- ### Cookies
90
-
91
- ```js
92
- // Cookie: token=abc; theme=dark
93
- request.cookies.token // "abc"
94
- request.cookies.theme // "dark"
95
- ```
96
-
97
- ## `NextResponse`
98
-
99
- Imported from the framework (same API as the Next.js App Router):
100
-
101
- ```js
102
- const { NextResponse } = require('../_framework/next-response');
103
-
104
- GET: async () => NextResponse.json({ users: [] }, { status: 200 });
105
-
106
- POST: async () => NextResponse.json({ error: 'Bad input' }, { status: 400 });
107
-
108
- GET: async () => NextResponse.text('hello');
109
-
110
- GET: async () => NextResponse.html('<h1>Hi</h1>');
111
-
112
- GET: async () => NextResponse.redirect('/login', 307);
113
-
114
- GET: async () => NextResponse.json({ err: 'nope' }, { status: 500 });
115
- ```
116
-
117
- ### Cookies with `NextResponse`
118
-
119
- ```js
120
- GET: async () => {
121
- const res = NextResponse.json({ ok: true });
122
- res.cookies.set('session', 'abc123', {
123
- httpOnly: true,
124
- secure: true,
125
- maxAge: 60 * 60 * 24 * 7, // 7 days (seconds)
126
- path: '/',
127
- });
128
- res.cookies.delete('old-cookie');
129
- return res;
130
- };
131
- ```
132
-
133
- Headers are emitted as `Set-Cookie` (multiple allowed).
134
-
135
- ## Error handling
136
-
137
- Throw — the framework maps it to a status:
138
-
139
- ```js
140
- const err = new Error('User not found');
141
- err.status = 404;
142
- throw err; // → 404 { error: 'User not found' }
143
-
144
- throw new SyntaxError('…'); // → 400
145
- throw new Error('boom'); // → 500
146
- ```
147
-
148
- `await request.json()` on a malformed body throws a `SyntaxError` with `status = 400` automatically.
149
-
150
- Unhandled errors return `500` with `{"error": "…"}` and are logged to the server console.
151
-
152
- ## Method not allowed → 405
153
-
154
- If a route exists but doesn't export your method:
155
-
156
- ```
157
- DELETE /api/hello → 405
158
- Allow: GET
159
- { "error": "Method DELETE not allowed", "allowed": ["GET"] }
160
- ```
161
-
162
- `HEAD` falls back to your `GET` handler automatically (response body suppressed). `OPTIONS` returns `204` with an `Allow` header and CORS preflight headers — useful for cross-origin clients.
163
-
164
- ## CORS
165
-
166
- API responses automatically include:
167
-
168
- ```
169
- Access-Control-Allow-Origin: *
170
- Access-Control-Allow-Methods: GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS
171
- Access-Control-Allow-Headers: Content-Type, Authorization
172
- ```
173
-
174
- ## Native ESM (`.mjs`)
175
-
176
- Server files are CommonJS by default. Use the `.mjs` extension for native ESM:
177
-
178
- ```js
179
- // api/todos.mjs
180
- import { NextResponse } from '../_framework/next-response.js';
181
-
182
- export async function GET() {
183
- return NextResponse.json({ todos: [] });
184
- }
185
- ```
186
-
187
- You can also `export` directly from a `.js` file — the loader falls back to ESM if `require()` reports `ERR_REQUIRE_ESM`.
188
-
189
- ## Legacy `(req, res)` handlers
190
-
191
- Express/Next-pages style still works — export a function and write the response yourself:
192
-
193
- ```js
194
- // api/legacy.js
195
- module.exports = function (req, res) {
196
- res.status(200).json({ legacy: true, method: req.method });
197
- };
198
- ```
199
-
200
- Available on `res`: `status(code)`, `json(data)`, `send(data)`, `setHeader(k, v)`, `redirect(url, code?)`. The `request` passed in is enriched with `params`, `cookies`, `query`, and `nextUrl`.
201
-
202
- The framework falls back to a `500` if a legacy handler never sends a response.
203
-
204
- ## Hot reload
205
-
206
- Editing files under `api/`, `lib/`, or `models/` triggers a reload: the server clears the require cache for project files (not `node_modules`) and re-imports your routes. Save → the next request sees new code.
207
-
208
- ## Continue
209
-
210
- → [API Client](./07-api-client.md)
1
+ # 06 · API Routes
2
+
3
+ API routes are the **server side** of your app — real Node.js handlers you call over HTTP. They're how you fetch data, handle forms, and run database queries without exposing secrets in the browser.
4
+
5
+ ## Where routes live
6
+
7
+ Two equivalent layouts, both producing `/api/**` URLs:
8
+
9
+ ```
10
+ api/users.js → /api/users (classic)
11
+ api/users/[id].js → /api/users/:id
12
+ app/api/users/route.js → /api/users (Next.js style)
13
+ app/api/users/[id]/route.js → /api/users/:id
14
+ ```
15
+
16
+ - **Classic:** `api/<name>.js` → `/api/<name>`; folders nest: `api/posts/[id]/comments.js`.
17
+ - **Next style:** any `route.js` under `app/api/` → the folder path mapped to `/api/...`.
18
+
19
+ Both can coexist. Use whichever reads better.
20
+
21
+ ## Your first handler
22
+
23
+ Export functions named after HTTP methods:
24
+
25
+ ```js
26
+ // api/hello.js → /api/hello
27
+ module.exports = {
28
+ GET: async (request) => {
29
+ const name = request.query.get('name') || 'world';
30
+ return { hello: name };
31
+ },
32
+
33
+ POST: async (request) => {
34
+ const body = await request.json();
35
+ return { created: true, ...body };
36
+ },
37
+ };
38
+ ```
39
+
40
+ **Return a value — don't manage a raw socket.** The framework turns it into the HTTP response:
41
+
42
+ | You return | Response |
43
+ |------------|----------|
44
+ | `{ ok: true }` / `[]` | `200` + JSON |
45
+ | `'some text'` | `200` + `text/plain` |
46
+ | `NextResponse.json(data, { status: 400 })` | `400` + your headers |
47
+ | `NextResponse.redirect('/login')` | `307` + `Location` |
48
+ | `undefined` / `null` | `500` + a helpful error (you forgot to return) |
49
+
50
+ ## The `request` object
51
+
52
+ ```js
53
+ GET: async (request) => {
54
+ request.method // 'GET'
55
+ request.url // full URL
56
+ request.params // { id: '42' } from [id] segments
57
+ request.query // URLSearchParams → get('page'), getAll('tag')
58
+ request.headers // raw Node headers (lowercase keys)
59
+ request.cookies // { token: '…' } parsed from Cookie header
60
+ request.rawBody // raw body string
61
+ request.body // parsed JSON if content-type was JSON
62
+
63
+ await request.json() // parse JSON body (throws 400 on invalid)
64
+ await request.text() // raw text
65
+ await request.formData()// urlencoded → { field: 'value' }
66
+ }
67
+ ```
68
+
69
+ ### Dynamic and catch-all params
70
+
71
+ ```js
72
+ // api/users/[id].js
73
+ GET: async (request) => request.params.id // /api/users/7 → "7"
74
+
75
+ // api/files/[...path].js
76
+ GET: async (request) => request.params.path // /api/files/a/b → ['a','b']
77
+ ```
78
+
79
+ The literal prefix is validated — `api/files/[...path]` won't match `/api/other/x`.
80
+
81
+ ### Query strings
82
+
83
+ ```js
84
+ // GET /api/search?q=aziz&tag=a&tag=b
85
+ request.query.get('q') // "aziz"
86
+ request.query.getAll('tag') // ["a", "b"]
87
+ ```
88
+
89
+ ### Cookies
90
+
91
+ ```js
92
+ // Cookie: token=abc; theme=dark
93
+ request.cookies.token // "abc"
94
+ request.cookies.theme // "dark"
95
+ ```
96
+
97
+ ## `NextResponse`
98
+
99
+ Imported from the framework (same API as the Next.js App Router):
100
+
101
+ ```js
102
+ const { NextResponse } = require('../_framework/next-response');
103
+
104
+ GET: async () => NextResponse.json({ users: [] }, { status: 200 });
105
+
106
+ POST: async () => NextResponse.json({ error: 'Bad input' }, { status: 400 });
107
+
108
+ GET: async () => NextResponse.text('hello');
109
+
110
+ GET: async () => NextResponse.html('<h1>Hi</h1>');
111
+
112
+ GET: async () => NextResponse.redirect('/login', 307);
113
+
114
+ GET: async () => NextResponse.json({ err: 'nope' }, { status: 500 });
115
+ ```
116
+
117
+ ### Cookies with `NextResponse`
118
+
119
+ ```js
120
+ GET: async () => {
121
+ const res = NextResponse.json({ ok: true });
122
+ res.cookies.set('session', 'abc123', {
123
+ httpOnly: true,
124
+ secure: true,
125
+ maxAge: 60 * 60 * 24 * 7, // 7 days (seconds)
126
+ path: '/',
127
+ });
128
+ res.cookies.delete('old-cookie');
129
+ return res;
130
+ };
131
+ ```
132
+
133
+ Headers are emitted as `Set-Cookie` (multiple allowed).
134
+
135
+ ## Error handling
136
+
137
+ Throw — the framework maps it to a status:
138
+
139
+ ```js
140
+ const err = new Error('User not found');
141
+ err.status = 404;
142
+ throw err; // → 404 { error: 'User not found' }
143
+
144
+ throw new SyntaxError('…'); // → 400
145
+ throw new Error('boom'); // → 500
146
+ ```
147
+
148
+ `await request.json()` on a malformed body throws a `SyntaxError` with `status = 400` automatically.
149
+
150
+ Unhandled errors return `500` with `{"error": "…"}` and are logged to the server console.
151
+
152
+ ## Method not allowed → 405
153
+
154
+ If a route exists but doesn't export your method:
155
+
156
+ ```
157
+ DELETE /api/hello → 405
158
+ Allow: GET
159
+ { "error": "Method DELETE not allowed", "allowed": ["GET"] }
160
+ ```
161
+
162
+ `HEAD` falls back to your `GET` handler automatically (response body suppressed). `OPTIONS` returns `204` with an `Allow` header and CORS preflight headers — useful for cross-origin clients.
163
+
164
+ ## CORS
165
+
166
+ API responses automatically include:
167
+
168
+ ```
169
+ Access-Control-Allow-Origin: *
170
+ Access-Control-Allow-Methods: GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS
171
+ Access-Control-Allow-Headers: Content-Type, Authorization
172
+ ```
173
+
174
+ ## Native ESM (`.mjs`)
175
+
176
+ Server files are CommonJS by default. Use the `.mjs` extension for native ESM:
177
+
178
+ ```js
179
+ // api/todos.mjs
180
+ import { NextResponse } from '../_framework/next-response.js';
181
+
182
+ export async function GET() {
183
+ return NextResponse.json({ todos: [] });
184
+ }
185
+ ```
186
+
187
+ You can also `export` directly from a `.js` file — the loader falls back to ESM if `require()` reports `ERR_REQUIRE_ESM`.
188
+
189
+ ## Legacy `(req, res)` handlers
190
+
191
+ Express/Next-pages style still works — export a function and write the response yourself:
192
+
193
+ ```js
194
+ // api/legacy.js
195
+ module.exports = function (req, res) {
196
+ res.status(200).json({ legacy: true, method: req.method });
197
+ };
198
+ ```
199
+
200
+ Available on `res`: `status(code)`, `json(data)`, `send(data)`, `setHeader(k, v)`, `redirect(url, code?)`. The `request` passed in is enriched with `params`, `cookies`, `query`, and `nextUrl`.
201
+
202
+ The framework falls back to a `500` if a legacy handler never sends a response.
203
+
204
+ ## Hot reload
205
+
206
+ Editing files under `api/`, `lib/`, or `models/` triggers a reload: the server clears the require cache for project files (not `node_modules`) and re-imports your routes. Save → the next request sees new code.
207
+
208
+ ## Continue
209
+
210
+ → [API Client](./07-api-client.md)