@aggiovato/yrest 0.1.1 → 0.2.1

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/README.md CHANGED
@@ -7,23 +7,23 @@ Define your data in a `db.yml` file and get a fully functional CRUD REST API in
7
7
  ## Install
8
8
 
9
9
  ```bash
10
- npm install -D yrest
10
+ npm install -D @aggiovato/yrest
11
11
  ```
12
12
 
13
13
  Or run directly with npx:
14
14
 
15
15
  ```bash
16
- npx yrest serve db.yml
16
+ npx @aggiovato/yrest serve db.yml
17
17
  ```
18
18
 
19
19
  ## Quick start
20
20
 
21
21
  ```bash
22
- # Create a sample db.yml in the current directory
23
- npx yrest init
22
+ # Create a sample db.yml and yrest.config.yml in the current directory
23
+ npx @aggiovato/yrest init
24
24
 
25
25
  # Start the server
26
- npx yrest serve db.yml
26
+ npx @aggiovato/yrest serve db.yml
27
27
  ```
28
28
 
29
29
  ```txt
@@ -34,44 +34,80 @@ Resources (base: /):
34
34
  /posts
35
35
  ```
36
36
 
37
+ Open `http://localhost:3070/_about` in your browser for a live overview of all generated endpoints.
38
+
39
+ ---
40
+
37
41
  ## Commands
38
42
 
39
43
  ### `init`
40
44
 
41
- Creates a sample `db.yml` in the current directory.
45
+ Creates a sample `db.yml` and a `yrest.config.yml` template in the current directory.
42
46
 
43
47
  ```bash
44
- yrest init # basic sample (default)
45
- yrest init --sample relational # with _rel relations
46
- yrest init --file api.yml # custom filename
47
- yrest init --sample relational --file api.yml
48
+ npx @aggiovato/yrest init # basic sample (default)
49
+ npx @aggiovato/yrest init --sample relational # with _rel relations
50
+ npx @aggiovato/yrest init --file api.yml # custom filename
51
+ npx @aggiovato/yrest init --sample relational --file api.yml
48
52
  ```
49
53
 
50
- | Flag | Default | Description |
51
- |------|---------|-------------|
52
- | `--file` | `db.yml` | Output filename |
53
- | `--sample` | `basic` | Sample data (`basic`, `relational`) |
54
+ | Flag | Default | Description |
55
+ | ---------- | -------- | ----------------------------------- |
56
+ | `--file` | `db.yml` | Output filename |
57
+ | `--sample` | `basic` | Sample data (`basic`, `relational`) |
54
58
 
55
59
  **Samples:**
60
+
56
61
  - `basic` — two independent collections: `users` and `products`
57
62
  - `relational` — three collections with `_rel` relationships: `users`, `posts` and `comments`
58
63
 
64
+ ---
65
+
59
66
  ### `serve`
60
67
 
61
68
  Starts the mock server.
62
69
 
63
70
  ```bash
64
- yrest serve db.yml
65
- yrest serve db.yml --port 3001
66
- yrest serve db.yml --host 0.0.0.0
67
- yrest serve db.yml --base /api
71
+ npx @aggiovato/yrest serve db.yml
72
+ npx @aggiovato/yrest serve db.yml --port 3001 --host 0.0.0.0
73
+ npx @aggiovato/yrest serve db.yml --base /api --watch
74
+ npx @aggiovato/yrest serve db.yml --readonly --delay 300
75
+ npx @aggiovato/yrest serve db.yml --pageable 20
68
76
  ```
69
77
 
70
- | Flag | Default | Description |
71
- |------|---------|-------------|
72
- | `--port` | `3070` | Port to listen on |
73
- | `--host` | `localhost` | Host to bind |
74
- | `--base` | _(none)_ | Prefix for all routes |
78
+ | Flag | Default | Description |
79
+ | ---------------- | ----------- | ----------------------------------------------------------------------- |
80
+ | `--port` | `3070` | Port to listen on |
81
+ | `--host` | `localhost` | Host to bind |
82
+ | `--base` | _(none)_ | Prefix for all routes (e.g. `/api`) |
83
+ | `--watch` | `false` | Reload `db.yml` automatically when it changes on disk |
84
+ | `--readonly` | `false` | Reject all write operations (POST, PUT, PATCH, DELETE) with `405` |
85
+ | `--delay <ms>` | `0` | Add a fixed delay to all responses (simulates network latency) |
86
+ | `--pageable [n]` | `false` | Wrap GET collection responses in `{ data, pagination }`. Optional limit |
87
+
88
+ All flags can also be set in `yrest.config.yml` (see below). CLI flags always take priority over the config file.
89
+
90
+ ---
91
+
92
+ ## Configuration file
93
+
94
+ `yrest init` creates a `yrest.config.yml` alongside `db.yml`. Options defined here apply every time you run `serve` without needing to type flags:
95
+
96
+ ```yaml
97
+ # yrest.config.yml
98
+ file: db.yml
99
+ port: 3070
100
+ host: localhost
101
+ # base: /api
102
+ # watch: false
103
+ # readonly: false
104
+ # delay: 0
105
+ # pageable: false # true (limit 10), or a number (custom limit)
106
+ ```
107
+
108
+ **Priority order** (highest wins): CLI flags → `yrest.config.yml` → schema defaults.
109
+
110
+ ---
75
111
 
76
112
  ## Database format
77
113
 
@@ -92,6 +128,8 @@ posts:
92
128
 
93
129
  Each top-level key becomes a resource with full CRUD endpoints.
94
130
 
131
+ ---
132
+
95
133
  ## Generated endpoints
96
134
 
97
135
  For each resource in `db.yml`:
@@ -107,16 +145,193 @@ DELETE /users/:id Delete
107
145
 
108
146
  With `--base /api` all routes are prefixed: `/api/users`, `/api/users/:id`, etc.
109
147
 
148
+ ---
149
+
150
+ ## Query params
151
+
152
+ All query params can be combined freely.
153
+
154
+ ### Filtering
155
+
156
+ Return only items that match one or more field values:
157
+
158
+ ```txt
159
+ GET /users?name=Ana
160
+ GET /users?role=admin&active=true
161
+ ```
162
+
163
+ Comparison is case-sensitive and converts types to string (`?id=1` matches numeric `id: 1`).
164
+
165
+ ### Sorting
166
+
167
+ ```txt
168
+ GET /users?_sort=name # ascending (default)
169
+ GET /users?_sort=name&_order=desc # descending
170
+ ```
171
+
172
+ String fields are compared case-insensitively. Items missing the sort field are pushed to the end.
173
+
174
+ ### Pagination
175
+
176
+ **Without `--pageable`** (default):
177
+
178
+ ```txt
179
+ GET /users?_page=1&_limit=10 # page 1, 10 items per page
180
+ GET /users?_limit=5 # first 5 items
181
+ ```
182
+
183
+ When `_page` or `_limit` are used, the response includes an `X-Total-Count` header with the total number of items before pagination.
184
+
185
+ **With `--pageable`** (or `pageable: true` in config):
186
+
187
+ Every GET collection response is automatically wrapped in a `{ data, pagination }` envelope:
188
+
189
+ ```bash
190
+ npx @aggiovato/yrest serve db.yml --pageable # default limit: 10
191
+ npx @aggiovato/yrest serve db.yml --pageable 20 # custom limit: 20
192
+ ```
193
+
194
+ ```json
195
+ {
196
+ "data": [
197
+ { "id": 1, "name": "Ana" },
198
+ { "id": 2, "name": "Luis" }
199
+ ],
200
+ "pagination": {
201
+ "page": 1,
202
+ "limit": 10,
203
+ "totalItems": 23,
204
+ "totalPages": 3,
205
+ "isFirst": true,
206
+ "isLast": false,
207
+ "hasNext": true,
208
+ "hasPrev": false
209
+ }
210
+ }
211
+ ```
212
+
213
+ The `?_page` and `?_limit` query params still work in pageable mode to navigate pages.
214
+
215
+ ### Relation embedding (`?_expand`)
216
+
217
+ Embed a related parent object directly into the response using the `_rel` block (see [Relational data](#relational-data)):
218
+
219
+ ```txt
220
+ GET /posts?_expand=user # embed user object in each post
221
+ GET /posts/1?_expand=user # embed in a single item
222
+ ```
223
+
224
+ Both syntaxes are supported:
225
+
226
+ ```txt
227
+ ?_expand=author,category # comma-separated
228
+ ?_expand=author&_expand=category # repeated param
229
+ ```
230
+
231
+ Unresolvable keys are silently ignored. Works on all operations: GET, POST, PUT, PATCH, DELETE.
232
+
233
+ ### Combined example
234
+
235
+ ```txt
236
+ GET /posts?userId=1&_sort=title&_order=asc&_page=1&_limit=5&_expand=user
237
+ ```
238
+
239
+ Returns the first 5 posts by user 1, sorted alphabetically by title, with the user object embedded.
240
+
241
+ ---
242
+
243
+ ## Relational data
244
+
245
+ Use `_rel` to declare foreign key relationships between collections:
246
+
247
+ ```yaml
248
+ _rel:
249
+ posts:
250
+ userId: users
251
+
252
+ users:
253
+ - id: 1
254
+ name: Ana
255
+
256
+ posts:
257
+ - id: 1
258
+ title: First post
259
+ userId: 1
260
+ ```
261
+
262
+ This enables:
263
+
264
+ **Nested routes:**
265
+
266
+ ```txt
267
+ GET /users/1/posts # all posts where userId === 1
268
+ ```
269
+
270
+ **Relation embedding with `?_expand`:**
271
+
272
+ ```txt
273
+ GET /posts/1?_expand=user → { id: 1, title: "First post", userId: 1, user: { id: 1, name: "Ana" } }
274
+ ```
275
+
276
+ ---
277
+
278
+ ## Server modes
279
+
280
+ ### Watch mode
281
+
282
+ Automatically reloads `db.yml` when it changes on disk — useful when you edit the file manually while the server is running:
283
+
284
+ ```bash
285
+ npx @aggiovato/yrest serve db.yml --watch
286
+ ```
287
+
288
+ > **Note:** Watch mode reloads data in existing collections. Adding or removing entire collections requires a server restart.
289
+
290
+ ### Readonly mode
291
+
292
+ Rejects all write operations with `405 Method Not Allowed`:
293
+
294
+ ```bash
295
+ npx @aggiovato/yrest serve db.yml --readonly
296
+ ```
297
+
298
+ Useful to expose a stable read-only snapshot for demos or CI environments.
299
+
300
+ ### Delay mode
301
+
302
+ Adds a fixed delay (in milliseconds) to every response to simulate real network latency:
303
+
304
+ ```bash
305
+ npx @aggiovato/yrest serve db.yml --delay 500 # 500ms on every response
306
+ ```
307
+
308
+ ---
309
+
310
+ ## API overview page
311
+
312
+ Every running server exposes `GET /_about` — a self-contained HTML page listing all generated endpoints, active modes, query param reference and ready-to-run `curl` examples derived from your actual `db.yml`:
313
+
314
+ ```bash
315
+ open http://localhost:3070/_about
316
+ ```
317
+
318
+ The page reflects the live state of the server, so it updates automatically in watch mode.
319
+
320
+ ---
321
+
110
322
  ## HTTP responses
111
323
 
112
- | Status | When |
113
- |--------|------|
114
- | `200` | Successful GET, PUT, PATCH, DELETE |
115
- | `201` | Successful POST |
116
- | `404` | Resource or id not found |
117
- | `500` | Error reading or writing the YAML file |
324
+ | Status | When |
325
+ | ------ | -------------------------------------- |
326
+ | `200` | Successful GET, PUT, PATCH, DELETE |
327
+ | `201` | Successful POST |
328
+ | `404` | Resource or id not found |
329
+ | `405` | Write operation in readonly mode |
330
+ | `500` | Error reading or writing the YAML file |
118
331
 
119
- DELETE returns the deleted item (useful for debugging).
332
+ DELETE returns the deleted item as confirmation.
333
+
334
+ ---
120
335
 
121
336
  ## ID generation
122
337
 
@@ -130,11 +345,20 @@ All write operations (POST, PUT, PATCH, DELETE) are saved back to `db.yml` immed
130
345
 
131
346
  CORS is enabled by default, so you can call the API from any frontend running on a different port without extra configuration.
132
347
 
348
+ ---
349
+
133
350
  ## Frontend usage
134
351
 
135
352
  ```ts
136
- // List
137
- const users = await fetch("http://localhost:3070/users").then(r => r.json());
353
+ // List all
354
+ const users = await fetch("http://localhost:3070/users").then((r) => r.json());
355
+
356
+ // Filter + sort + paginate
357
+ const res = await fetch("http://localhost:3070/users?role=admin&_sort=name&_page=1&_limit=10");
358
+
359
+ // Embed related object
360
+ const post = await fetch("http://localhost:3070/posts/1?_expand=user").then((r) => r.json());
361
+ // → { id: 1, title: "...", userId: 1, user: { id: 1, name: "Ana" } }
138
362
 
139
363
  // Create
140
364
  await fetch("http://localhost:3070/users", {
@@ -159,11 +383,114 @@ await fetch("http://localhost:3070/users/1", { method: "DELETE" });
159
383
  ```json
160
384
  {
161
385
  "scripts": {
162
- "mock": "yrest serve db.yml"
386
+ "mock": "yrest serve db.yml",
387
+ "mock:watch": "yrest serve db.yml --watch"
163
388
  }
164
389
  }
165
390
  ```
166
391
 
392
+ ---
393
+
394
+ ## Contributing
395
+
396
+ ### Prerequisites
397
+
398
+ - Node.js >= 20
399
+ - [Task](https://taskfile.dev) — task runner (`brew install go-task` / `scoop install task` / [other methods](https://taskfile.dev/installation/))
400
+
401
+ ### Task commands
402
+
403
+ Run `task --list` to see all available commands.
404
+
405
+ #### Development
406
+
407
+ | Command | What it does |
408
+ | ----------------------- | ---------------------------------------------------------------------- |
409
+ | `task test` | Runs the full test suite once |
410
+ | `task test:watch` | Runs tests in watch mode — reruns on every file change |
411
+ | `task build` | Compiles TypeScript to `dist/` via tsup |
412
+ | `task dev` | Builds once, then starts watch-build + server from `dist/` in parallel |
413
+ | `task serve:dist` | Builds and starts the server from the local `dist/` |
414
+ | `task serve:dist:watch` | Builds and starts the server with `--watch` (reloads db.yml on change) |
415
+ | `task serve:npx` | Starts the published npm version (useful to compare against local) |
416
+ | `task serve:npx:watch` | Starts the published npm version with `--watch` |
417
+ | `task preflight` | Full pre-push check: format, lint, typecheck and tests in order |
418
+
419
+ #### Release
420
+
421
+ | Command | What it does |
422
+ | -------------------- | -------------------------------------------------------------- |
423
+ | `task release:patch` | Bumps `x.x.N`, creates a git commit and tag |
424
+ | `task release:minor` | Bumps `x.N.0`, creates a git commit and tag |
425
+ | `task release:major` | Bumps `N.0.0`, creates a git commit and tag |
426
+ | `task publish` | Runs tests + build and publishes to npm (requires `npm login`) |
427
+
428
+ ### Development workflow
429
+
430
+ **Day-to-day work:**
431
+
432
+ ```bash
433
+ task test:watch # keep this running in one terminal
434
+ task dev # keep this running in another terminal
435
+ ```
436
+
437
+ `test:watch` reruns the suite on every save so you catch regressions immediately. `dev` rebuilds and serves so you can call the endpoints manually while you work.
438
+
439
+ **Before pushing:**
440
+
441
+ ```bash
442
+ task preflight # format + lint + typecheck + tests in one command
443
+ ```
444
+
445
+ **Good practices:**
446
+
447
+ - Write tests for every new feature or bug fix before opening a PR.
448
+ - Keep `db.yml` in a valid state — it is used as the default file when running local servers.
449
+ - `dist/` is gitignored and generated at build time; never commit it manually.
450
+ - Version bumps are done with `task release:*`, not by editing `package.json` directly — the Task command also creates the git tag that triggers the publish pipeline.
451
+
452
+ ### Release workflow
453
+
454
+ Releases are fully automated via GitHub Actions once a version tag is pushed:
455
+
456
+ ```bash
457
+ # 1. Make sure everything is clean
458
+ task preflight
459
+
460
+ # 2. Bump the version (choose one)
461
+ task release:patch # bug fixes
462
+ task release:minor # new features, backwards compatible
463
+ task release:major # breaking changes
464
+
465
+ # 3. Push the commit and the tag
466
+ git push && git push --tags
467
+ ```
468
+
469
+ Step 3 triggers two GitHub Actions pipelines automatically:
470
+
471
+ - **CI** — runs on every push to `main` and on every PR. Executes typecheck + tests on Node 20 and Node 22.
472
+ - **Publish** — runs only when a `v*` tag is pushed. Runs tests + build and publishes to npm using Trusted Publishing (OIDC — no tokens stored as secrets).
473
+
474
+ ### CI/CD pipelines
475
+
476
+ ```
477
+ push to main / PR open
478
+
479
+
480
+ [CI workflow]
481
+ ├── Node 20: lint + format check + typecheck + tests
482
+ └── Node 22: lint + format check + typecheck + tests
483
+
484
+ push tag v*
485
+
486
+
487
+ [Publish workflow]
488
+ ├── tests + build (via prepublishOnly)
489
+ └── npm publish --provenance (via Trusted Publishing / OIDC, Node 24)
490
+ ```
491
+
492
+ ---
493
+
167
494
  ## License
168
495
 
169
496
  MIT