@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 +360 -33
- package/dist/cli/index.js +569 -59
- package/dist/cli/index.mjs +569 -59
- package/dist/index.d.mts +124 -3
- package/dist/index.d.ts +124 -3
- package/dist/index.js +494 -44
- package/dist/index.mjs +494 -44
- package/package.json +13 -4
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
|
|
51
|
-
|
|
52
|
-
| `--file`
|
|
53
|
-
| `--sample` | `basic`
|
|
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 --
|
|
67
|
-
yrest serve db.yml --
|
|
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
|
|
71
|
-
|
|
72
|
-
| `--port`
|
|
73
|
-
| `--host`
|
|
74
|
-
| `--base`
|
|
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`
|
|
115
|
-
| `201`
|
|
116
|
-
| `404`
|
|
117
|
-
| `
|
|
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
|
|
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
|