@yrest/cli 0.8.1 → 0.10.0
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 +195 -45
- package/dist/cli/index.js +1241 -209
- package/dist/cli/index.mjs +1238 -206
- package/dist/index.d.mts +91 -5
- package/dist/index.d.ts +91 -5
- package/dist/index.js +814 -188
- package/dist/index.mjs +811 -185
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
[](https://github.com/aggiovato/yRest/actions)
|
|
11
11
|
[](https://www.npmjs.com/package/@yrest/cli)
|
|
12
12
|
[](https://www.typescriptlang.org/)
|
|
13
|
-
[](https://socket.dev/npm/package/@yrest/cli)
|
|
14
14
|
|
|
15
15
|
YAML-powered json-server alternative. Zero-config REST API mock server with full CRUD, relations, filters and snapshots from a `db.yml` file.
|
|
16
16
|
|
|
@@ -48,28 +48,32 @@ DELETE /users/1 → 200 OK
|
|
|
48
48
|
|
|
49
49
|
A YAML-first alternative to json-server for frontend development.
|
|
50
50
|
|
|
51
|
-
| Feature
|
|
52
|
-
|
|
|
53
|
-
| YAML database
|
|
54
|
-
| Zero config
|
|
55
|
-
| Full CRUD
|
|
56
|
-
| Field operators (`_gte`, `_like`, `_regex`…)
|
|
57
|
-
| Full-text search
|
|
58
|
-
| Relations
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
51
|
+
| Feature | yrest | json-server |
|
|
52
|
+
| --------------------------------------------------- | :---: | :---------: |
|
|
53
|
+
| YAML database | ✅ | ❌ |
|
|
54
|
+
| Zero config | ✅ | ✅ |
|
|
55
|
+
| Full CRUD | ✅ | ✅ |
|
|
56
|
+
| Field operators (`_gte`, `_like`, `_regex`…) | ✅ | ⚠️ |
|
|
57
|
+
| Full-text search | ✅ | ✅ |
|
|
58
|
+
| Relations: many2one, one2one, many2many | ✅ | ⚠️ |
|
|
59
|
+
| Nested routes + bidirectional many2many | ✅ | ✅ |
|
|
60
|
+
| Auto-embedding (`nested: true`) | ✅ | ❌ |
|
|
61
|
+
| Field projection (`_fields`) | ✅ | ❌ |
|
|
62
|
+
| Pageable mode (envelope response) | ✅ | ❌ |
|
|
63
|
+
| Custom static routes (`_routes`) | ✅ | ❌ |
|
|
64
|
+
| Template variables in responses | ✅ | ❌ |
|
|
65
|
+
| Handler functions (JS logic) | ✅ | ❌ |
|
|
66
|
+
| Conditional scenarios (`scenarios:`) | ✅ | ❌ |
|
|
67
|
+
| Snapshot endpoints | ✅ | ❌ |
|
|
68
|
+
| Config file | ✅ | ⚠️ |
|
|
69
|
+
| API overview page (`/_about`) | ✅ | ❌ |
|
|
70
|
+
| Watch mode | ✅ | ✅ |
|
|
71
|
+
| Readonly mode | ✅ | ❌ |
|
|
72
|
+
| Atomic writes | ✅ | ✅ |
|
|
73
|
+
| TypeScript types | ✅ | ❌ |
|
|
74
|
+
| Programmatic API for test frameworks | ✅ | ❌ |
|
|
75
|
+
| OpenAPI 3.0 spec (`GET /_openapi`, `yrest openapi`) | ✅ | ❌ |
|
|
76
|
+
| Field annotations (`_schema`) | ✅ | ❌ |
|
|
73
77
|
|
|
74
78
|
---
|
|
75
79
|
|
|
@@ -125,15 +129,41 @@ npx @yrest/cli init --file api.yml # custom filename
|
|
|
125
129
|
npx @yrest/cli init --sample relational --file api.yml
|
|
126
130
|
```
|
|
127
131
|
|
|
128
|
-
| Flag | Default | Description
|
|
129
|
-
| ---------- | -------- |
|
|
130
|
-
| `--file` | `db.yml` | Output filename
|
|
131
|
-
| `--sample` | `basic` | Sample data (`basic`, `relational`) |
|
|
132
|
+
| Flag | Default | Description |
|
|
133
|
+
| ---------- | -------- | ------------------------------------------------ |
|
|
134
|
+
| `--file` | `db.yml` | Output filename |
|
|
135
|
+
| `--sample` | `basic` | Sample data (`basic`, `relational`, `ecommerce`) |
|
|
132
136
|
|
|
133
137
|
**Samples:**
|
|
134
138
|
|
|
135
|
-
- `basic` —
|
|
136
|
-
- `relational` —
|
|
139
|
+
- `basic` — three independent collections: `users`, `products` and `categories`
|
|
140
|
+
- `relational` — blog domain with all three relation types: `users`, `posts`, `comments`, `tags` and a pivot table
|
|
141
|
+
- `ecommerce` — e-commerce domain with products, orders, reviews and custom `_routes` (scenarios, template vars, error injection)
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
### `openapi`
|
|
146
|
+
|
|
147
|
+
Generates an OpenAPI 3.0.3 spec from a `db.yml` file without starting a server.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
npx @yrest/cli openapi db.yml # writes openapi.yaml
|
|
151
|
+
npx @yrest/cli openapi db.yml --format json # writes openapi.json
|
|
152
|
+
npx @yrest/cli openapi db.yml --stdout # prints to stdout
|
|
153
|
+
npx @yrest/cli openapi db.yml --output api-spec.yaml # custom output path
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
| Flag | Default | Description |
|
|
157
|
+
| ----------------- | ----------- | ----------------------------------------------------------- |
|
|
158
|
+
| `--output <file>` | _(auto)_ | Output file path (default: `openapi.yaml` / `openapi.json`) |
|
|
159
|
+
| `--format <fmt>` | `yaml` | Output format: `yaml` or `json` |
|
|
160
|
+
| `--stdout` | `false` | Print to stdout instead of writing a file |
|
|
161
|
+
| `--base <base>` | _(none)_ | Base path prefix applied to all routes |
|
|
162
|
+
| `--port <port>` | `3070` | Server port shown in the `servers` block |
|
|
163
|
+
| `--host <host>` | `localhost` | Server host shown in the `servers` block |
|
|
164
|
+
| `--title <title>` | `yRest API` | API title for the `info` block |
|
|
165
|
+
|
|
166
|
+
The spec is also available live at `GET /_openapi` (YAML) and `GET /_openapi.json` (JSON) while the server is running — no extra flag or config needed.
|
|
137
167
|
|
|
138
168
|
---
|
|
139
169
|
|
|
@@ -212,6 +242,46 @@ Each top-level key becomes a resource with full CRUD endpoints.
|
|
|
212
242
|
|
|
213
243
|
---
|
|
214
244
|
|
|
245
|
+
## Field annotations (`_schema`)
|
|
246
|
+
|
|
247
|
+
Add a `_schema` block to `db.yml` to declare field-level metadata per collection. Used by the OpenAPI generator to produce accurate schemas — has no effect on runtime CRUD behavior.
|
|
248
|
+
|
|
249
|
+
```yaml
|
|
250
|
+
_schema:
|
|
251
|
+
users:
|
|
252
|
+
name: required # shorthand: marks field as required
|
|
253
|
+
email:
|
|
254
|
+
required: true
|
|
255
|
+
format: email
|
|
256
|
+
age:
|
|
257
|
+
type: integer
|
|
258
|
+
role:
|
|
259
|
+
type: string
|
|
260
|
+
enum: [admin, editor, viewer]
|
|
261
|
+
default: viewer
|
|
262
|
+
description: User role
|
|
263
|
+
|
|
264
|
+
users:
|
|
265
|
+
- id: 1
|
|
266
|
+
name: Ana
|
|
267
|
+
email: ana@test.com
|
|
268
|
+
age: 28
|
|
269
|
+
role: admin
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
| Key | Type | Description |
|
|
273
|
+
| ------------- | --------- | ----------------------------------------------------------------------------------------- |
|
|
274
|
+
| `required` | `boolean` | Marks the field as required in the OpenAPI schema |
|
|
275
|
+
| `type` | `string` | Overrides the inferred type (`string`, `integer`, `number`, `boolean`, `array`, `object`) |
|
|
276
|
+
| `format` | `string` | OpenAPI format hint (`email`, `date`, `date-time`, `uuid`, `uri`, …) |
|
|
277
|
+
| `enum` | `array` | Restricts the field to a fixed set of values |
|
|
278
|
+
| `description` | `string` | Field description included in the OpenAPI schema |
|
|
279
|
+
| `default` | `any` | Default value shown in the OpenAPI schema |
|
|
280
|
+
|
|
281
|
+
Fields not listed in `_schema` are inferred from the first items in the collection and treated as optional. `_schema` itself is excluded from the generated REST resources.
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
215
285
|
## Generated endpoints
|
|
216
286
|
|
|
217
287
|
For each resource in `db.yml`:
|
|
@@ -392,29 +462,97 @@ Returns the first 5 posts by user 1, sorted alphabetically by title, with the us
|
|
|
392
462
|
|
|
393
463
|
## Relational data
|
|
394
464
|
|
|
395
|
-
Use `_rel` to declare
|
|
465
|
+
Use `_rel` to declare relationships between collections. Three relation types are supported.
|
|
466
|
+
|
|
467
|
+
### `many2one` (default)
|
|
468
|
+
|
|
469
|
+
A child record holds a foreign key pointing to a parent. The string shorthand implicitly declares `many2one`:
|
|
396
470
|
|
|
397
471
|
```yaml
|
|
398
472
|
_rel:
|
|
399
473
|
posts:
|
|
400
|
-
userId: users
|
|
474
|
+
userId: users # shorthand: many2one
|
|
475
|
+
# equivalent to:
|
|
476
|
+
# userId:
|
|
477
|
+
# type: many2one
|
|
478
|
+
# target: users
|
|
479
|
+
```
|
|
401
480
|
|
|
402
|
-
|
|
403
|
-
- id: 1
|
|
404
|
-
name: Ana
|
|
481
|
+
### `one2one`
|
|
405
482
|
|
|
406
|
-
|
|
483
|
+
One child record belongs to exactly one parent, and a parent has at most one child:
|
|
484
|
+
|
|
485
|
+
```yaml
|
|
486
|
+
_rel:
|
|
487
|
+
profiles:
|
|
488
|
+
userId:
|
|
489
|
+
type: one2one
|
|
490
|
+
target: users
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### `many2many`
|
|
494
|
+
|
|
495
|
+
Two collections are linked through a pivot (join) table:
|
|
496
|
+
|
|
497
|
+
```yaml
|
|
498
|
+
_rel:
|
|
499
|
+
posts:
|
|
500
|
+
tags:
|
|
501
|
+
type: many2many
|
|
502
|
+
target: tags
|
|
503
|
+
through: post_tags # pivot collection
|
|
504
|
+
foreignKey: postId
|
|
505
|
+
otherKey: tagId
|
|
506
|
+
|
|
507
|
+
post_tags:
|
|
407
508
|
- id: 1
|
|
408
|
-
|
|
409
|
-
|
|
509
|
+
postId: 1
|
|
510
|
+
tagId: 2
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### Auto-embedding with `nested: true`
|
|
514
|
+
|
|
515
|
+
Add `nested: true` to any relation to embed the related data automatically in every GET response, without needing `?_expand` or `?_embed`:
|
|
516
|
+
|
|
517
|
+
```yaml
|
|
518
|
+
_rel:
|
|
519
|
+
posts:
|
|
520
|
+
userId:
|
|
521
|
+
type: many2one
|
|
522
|
+
target: users
|
|
523
|
+
nested: true # user object embedded in every /posts response
|
|
524
|
+
tags:
|
|
525
|
+
type: many2many
|
|
526
|
+
target: tags
|
|
527
|
+
through: post_tags
|
|
528
|
+
foreignKey: postId
|
|
529
|
+
otherKey: tagId
|
|
530
|
+
nested: true # tags array embedded in every /posts response
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
```
|
|
534
|
+
GET /posts/1 → { id: 1, title: "...", userId: 1, user: { ... }, tags: [...] }
|
|
410
535
|
```
|
|
411
536
|
|
|
412
|
-
|
|
537
|
+
`many2one` / `one2one` with `nested: true` embed the parent object under the derived key (`userId` → `user`). The original FK field is preserved. `many2many` with `nested: true` embeds the resolved target array under the alias key.
|
|
413
538
|
|
|
414
|
-
|
|
539
|
+
### What relations enable
|
|
540
|
+
|
|
541
|
+
**Nested routes (many2one / one2one):**
|
|
415
542
|
|
|
416
543
|
```
|
|
417
|
-
GET /users/1/posts
|
|
544
|
+
GET /users/1/posts # all posts where userId === 1
|
|
545
|
+
GET /users/1/posts/3 # post 3 only if it belongs to user 1
|
|
546
|
+
GET /users/1/profiles # profile for user 1 (one2one: returns object, not array)
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
**Nested routes (many2many — bidirectional):**
|
|
550
|
+
|
|
551
|
+
Both directions are registered automatically from a single declaration:
|
|
552
|
+
|
|
553
|
+
```
|
|
554
|
+
GET /posts/1/tags # all tags linked to post 1 via pivot
|
|
555
|
+
GET /tags/2/posts # all posts linked to tag 2 via pivot (inverse — auto-created)
|
|
418
556
|
```
|
|
419
557
|
|
|
420
558
|
**Embed parent with `?_expand`:**
|
|
@@ -426,7 +564,9 @@ GET /posts/1?_expand=user → { id: 1, title: "First post", userId: 1, user:
|
|
|
426
564
|
**Embed children with `?_embed`:**
|
|
427
565
|
|
|
428
566
|
```
|
|
429
|
-
GET /users/1?_embed=posts
|
|
567
|
+
GET /users/1?_embed=posts → { id: 1, name: "Ana", posts: [...] }
|
|
568
|
+
GET /posts/1?_embed=tags → { id: 1, title: "...", tags: [...] } # many2many
|
|
569
|
+
GET /users/1?_embed=profiles → { id: 1, name: "Ana", profiles: { ... } } # one2one: object
|
|
430
570
|
```
|
|
431
571
|
|
|
432
572
|
---
|
|
@@ -742,15 +882,22 @@ Useful for test suites that need a clean reset between runs or demos that need a
|
|
|
742
882
|
|
|
743
883
|
---
|
|
744
884
|
|
|
745
|
-
##
|
|
885
|
+
## Meta endpoints
|
|
886
|
+
|
|
887
|
+
Every running server exposes the following meta endpoints without any extra configuration:
|
|
746
888
|
|
|
747
|
-
|
|
889
|
+
| Endpoint | Description |
|
|
890
|
+
| -------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
891
|
+
| `GET /_about` | Self-contained HTML page listing all endpoints, modes, query params and ready-to-run `curl` examples |
|
|
892
|
+
| `GET /_openapi` | OpenAPI 3.0.3 spec in YAML format — regenerated on every request |
|
|
893
|
+
| `GET /_openapi.json` | OpenAPI 3.0.3 spec in JSON format |
|
|
748
894
|
|
|
749
895
|
```bash
|
|
750
896
|
open http://localhost:3070/_about
|
|
897
|
+
curl http://localhost:3070/_openapi.json | npx swagger-ui-watcher -
|
|
751
898
|
```
|
|
752
899
|
|
|
753
|
-
|
|
900
|
+
Both `/_about` and the OpenAPI spec reflect the live storage state and update automatically in watch mode.
|
|
754
901
|
|
|
755
902
|
---
|
|
756
903
|
|
|
@@ -992,13 +1139,16 @@ const server = createYrestServer({
|
|
|
992
1139
|
| Visual panel (`/_panel`) | 🔜 |
|
|
993
1140
|
| Programmatic API for Vitest / Playwright | ✅ |
|
|
994
1141
|
| Docker image | 🔜 |
|
|
995
|
-
| OpenAPI export (`yrest openapi db.yml`) |
|
|
1142
|
+
| OpenAPI export (`yrest openapi db.yml`) | ✅ |
|
|
996
1143
|
| VS Code extension with YAML snippets | 🔜 |
|
|
997
1144
|
| Request validation with JSON Schema | 🔜 |
|
|
998
1145
|
| Conditional scenarios (`scenarios:`, `otherwise:`) | ✅ |
|
|
999
1146
|
| Per-route delay (`delay:`) | ✅ |
|
|
1000
1147
|
| Error injection (`error:` in `_routes`) | ✅ |
|
|
1001
1148
|
| Configurable ID strategy (`idStrategy`) | ✅ |
|
|
1149
|
+
| Explicit relation types (one2one, many2many) | ✅ |
|
|
1150
|
+
| Bidirectional many2many nested routes | ✅ |
|
|
1151
|
+
| Auto-embedding (`nested: true` in `_rel`) | ✅ |
|
|
1002
1152
|
|
|
1003
1153
|
---
|
|
1004
1154
|
|