@aggiovato/yrest 0.2.0 → 0.2.2

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 (2) hide show
  1. package/README.md +193 -39
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -19,7 +19,7 @@ npx @aggiovato/yrest serve db.yml
19
19
  ## Quick start
20
20
 
21
21
  ```bash
22
- # Create a sample db.yml in the current directory
22
+ # Create a sample db.yml and yrest.config.yml in the current directory
23
23
  npx @aggiovato/yrest init
24
24
 
25
25
  # Start the server
@@ -34,11 +34,15 @@ 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
48
  npx @aggiovato/yrest init # basic sample (default)
@@ -57,22 +61,53 @@ npx @aggiovato/yrest init --sample relational --file api.yml
57
61
  - `basic` — two independent collections: `users` and `products`
58
62
  - `relational` — three collections with `_rel` relationships: `users`, `posts` and `comments`
59
63
 
64
+ ---
65
+
60
66
  ### `serve`
61
67
 
62
68
  Starts the mock server.
63
69
 
64
70
  ```bash
65
71
  npx @aggiovato/yrest serve db.yml
66
- npx @aggiovato/yrest serve db.yml --port 3001
67
- npx @aggiovato/yrest serve db.yml --host 0.0.0.0
68
- npx @aggiovato/yrest serve db.yml --base /api
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
76
+ ```
77
+
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)
69
106
  ```
70
107
 
71
- | Flag | Default | Description |
72
- | -------- | ----------- | --------------------- |
73
- | `--port` | `3070` | Port to listen on |
74
- | `--host` | `localhost` | Host to bind |
75
- | `--base` | _(none)_ | Prefix for all routes |
108
+ **Priority order** (highest wins): CLI flags → `yrest.config.yml` → schema defaults.
109
+
110
+ ---
76
111
 
77
112
  ## Database format
78
113
 
@@ -93,6 +128,8 @@ posts:
93
128
 
94
129
  Each top-level key becomes a resource with full CRUD endpoints.
95
130
 
131
+ ---
132
+
96
133
  ## Generated endpoints
97
134
 
98
135
  For each resource in `db.yml`:
@@ -108,13 +145,15 @@ DELETE /users/:id Delete
108
145
 
109
146
  With `--base /api` all routes are prefixed: `/api/users`, `/api/users/:id`, etc.
110
147
 
148
+ ---
149
+
111
150
  ## Query params
112
151
 
113
152
  All query params can be combined freely.
114
153
 
115
154
  ### Filtering
116
155
 
117
- Return only items that match a field value:
156
+ Return only items that match one or more field values:
118
157
 
119
158
  ```txt
120
159
  GET /users?name=Ana
@@ -128,28 +167,78 @@ Comparison is case-sensitive and converts types to string (`?id=1` matches numer
128
167
  ```txt
129
168
  GET /users?_sort=name # ascending (default)
130
169
  GET /users?_sort=name&_order=desc # descending
131
- GET /users?_sort=id&_order=asc
132
170
  ```
133
171
 
134
172
  String fields are compared case-insensitively. Items missing the sort field are pushed to the end.
135
173
 
136
174
  ### Pagination
137
175
 
176
+ **Without `--pageable`** (default):
177
+
138
178
  ```txt
139
179
  GET /users?_page=1&_limit=10 # page 1, 10 items per page
140
180
  GET /users?_limit=5 # first 5 items
141
- GET /users?_page=2&_limit=5 # items 6–10
142
181
  ```
143
182
 
144
- When pagination is active, the response includes an `X-Total-Count` header with the total number of items before pagination (after any active filter).
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.
145
232
 
146
233
  ### Combined example
147
234
 
148
235
  ```txt
149
- GET /users?role=admin&_sort=name&_order=asc&_page=1&_limit=5
236
+ GET /posts?userId=1&_sort=title&_order=asc&_page=1&_limit=5&_expand=user
150
237
  ```
151
238
 
152
- Returns the first 5 admin users sorted alphabetically by name.
239
+ Returns the first 5 posts by user 1, sorted alphabetically by title, with the user object embedded.
240
+
241
+ ---
153
242
 
154
243
  ## Relational data
155
244
 
@@ -170,12 +259,66 @@ posts:
170
259
  userId: 1
171
260
  ```
172
261
 
173
- This enables nested routes:
262
+ This enables:
263
+
264
+ **Nested routes:**
174
265
 
175
266
  ```txt
176
267
  GET /users/1/posts # all posts where userId === 1
177
268
  ```
178
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
+
179
322
  ## HTTP responses
180
323
 
181
324
  | Status | When |
@@ -183,9 +326,12 @@ GET /users/1/posts # all posts where userId === 1
183
326
  | `200` | Successful GET, PUT, PATCH, DELETE |
184
327
  | `201` | Successful POST |
185
328
  | `404` | Resource or id not found |
329
+ | `405` | Write operation in readonly mode |
186
330
  | `500` | Error reading or writing the YAML file |
187
331
 
188
- DELETE returns the deleted item (useful for debugging).
332
+ DELETE returns the deleted item as confirmation.
333
+
334
+ ---
189
335
 
190
336
  ## ID generation
191
337
 
@@ -199,15 +345,21 @@ All write operations (POST, PUT, PATCH, DELETE) are saved back to `db.yml` immed
199
345
 
200
346
  CORS is enabled by default, so you can call the API from any frontend running on a different port without extra configuration.
201
347
 
348
+ ---
349
+
202
350
  ## Frontend usage
203
351
 
204
352
  ```ts
205
- // List
353
+ // List all
206
354
  const users = await fetch("http://localhost:3070/users").then((r) => r.json());
207
355
 
208
356
  // Filter + sort + paginate
209
357
  const res = await fetch("http://localhost:3070/users?role=admin&_sort=name&_page=1&_limit=10");
210
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" } }
362
+
211
363
  // Create
212
364
  await fetch("http://localhost:3070/users", {
213
365
  method: "POST",
@@ -231,7 +383,8 @@ await fetch("http://localhost:3070/users/1", { method: "DELETE" });
231
383
  ```json
232
384
  {
233
385
  "scripts": {
234
- "mock": "yrest serve db.yml"
386
+ "mock": "yrest serve db.yml",
387
+ "mock:watch": "yrest serve db.yml --watch"
235
388
  }
236
389
  }
237
390
  ```
@@ -251,14 +404,17 @@ Run `task --list` to see all available commands.
251
404
 
252
405
  #### Development
253
406
 
254
- | Command | What it does |
255
- | ----------------- | ------------------------------------------------------------------------------- |
256
- | `task test` | Runs the full test suite once |
257
- | `task test:watch` | Runs tests in watch mode — reruns on every file change |
258
- | `task build` | Compiles TypeScript to `dist/` via tsup |
259
- | `task dev` | Builds once, then starts watch-build + server from `dist/` in parallel |
260
- | `task serve:dist` | Builds and starts the server from the local `dist/` |
261
- | `task serve:npx` | Starts the version currently published on npm (useful to compare against local) |
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 |
262
418
 
263
419
  #### Release
264
420
 
@@ -280,14 +436,12 @@ task dev # keep this running in another terminal
280
436
 
281
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.
282
438
 
283
- **Before committing:**
439
+ **Before pushing:**
284
440
 
285
441
  ```bash
286
- task test # confirm everything passes
442
+ task preflight # format + lint + typecheck + tests in one command
287
443
  ```
288
444
 
289
- The test suite covers all routes, filters, sorting, pagination and CRUD operations. Do not commit with failing tests.
290
-
291
445
  **Good practices:**
292
446
 
293
447
  - Write tests for every new feature or bug fix before opening a PR.
@@ -300,8 +454,8 @@ The test suite covers all routes, filters, sorting, pagination and CRUD operatio
300
454
  Releases are fully automated via GitHub Actions once a version tag is pushed:
301
455
 
302
456
  ```bash
303
- # 1. Make sure all tests pass
304
- task test
457
+ # 1. Make sure everything is clean
458
+ task preflight
305
459
 
306
460
  # 2. Bump the version (choose one)
307
461
  task release:patch # bug fixes
@@ -312,10 +466,10 @@ task release:major # breaking changes
312
466
  git push && git push --tags
313
467
  ```
314
468
 
315
- Step 3 triggers two GitHub Actions pipelines automatically (no manual action needed after the push):
469
+ Step 3 triggers two GitHub Actions pipelines automatically:
316
470
 
317
471
  - **CI** — runs on every push to `main` and on every PR. Executes typecheck + tests on Node 20 and Node 22.
318
- - **Publish** — runs only when a `v*` tag is pushed. Runs tests + build and publishes to npm using Trusted Publishing (no tokens stored as secrets).
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).
319
473
 
320
474
  ### CI/CD pipelines
321
475
 
@@ -324,15 +478,15 @@ push to main / PR open
324
478
 
325
479
 
326
480
  [CI workflow]
327
- ├── Node 20: typecheck + tests
328
- └── Node 22: typecheck + tests
481
+ ├── Node 20: lint + format check + typecheck + tests
482
+ └── Node 22: lint + format check + typecheck + tests
329
483
 
330
484
  push tag v*
331
485
 
332
486
 
333
487
  [Publish workflow]
334
488
  ├── tests + build (via prepublishOnly)
335
- └── npm publish --provenance (via Trusted Publishing / OIDC)
489
+ └── npm publish --provenance (via Trusted Publishing / OIDC, Node 24)
336
490
  ```
337
491
 
338
492
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aggiovato/yrest",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Zero-config REST API mock server powered by a YAML file",
5
5
  "keywords": [
6
6
  "yaml",