@yrest/cli 0.8.0 → 0.9.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 CHANGED
@@ -10,7 +10,7 @@
10
10
  [![CI](https://github.com/aggiovato/yRest/actions/workflows/ci.yml/badge.svg)](https://github.com/aggiovato/yRest/actions)
11
11
  [![Node](https://img.shields.io/node/v/@yrest/cli)](https://www.npmjs.com/package/@yrest/cli)
12
12
  [![TypeScript](https://img.shields.io/badge/TypeScript-ready-blue)](https://www.typescriptlang.org/)
13
- [![Socket](https://badge.socket.dev/npm/package/@yrest/cli/0.8.0)](https://socket.dev/npm/package/@yrest/cli)
13
+ [![Socket](https://badge.socket.dev/npm/package/@yrest/cli/0.9.0)](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
 
@@ -55,7 +55,9 @@ A YAML-first alternative to json-server for frontend development.
55
55
  | Full CRUD | ✅ | ✅ |
56
56
  | Field operators (`_gte`, `_like`, `_regex`…) | ✅ | ⚠️ |
57
57
  | Full-text search | ✅ | ✅ |
58
- | Relations + nested routes | ✅ | |
58
+ | Relations: many2one, one2one, many2many | ✅ | ⚠️ |
59
+ | Nested routes + bidirectional many2many | ✅ | ✅ |
60
+ | Auto-embedding (`nested: true`) | ✅ | ❌ |
59
61
  | Field projection (`_fields`) | ✅ | ❌ |
60
62
  | Pageable mode (envelope response) | ✅ | ❌ |
61
63
  | Custom static routes (`_routes`) | ✅ | ❌ |
@@ -125,15 +127,16 @@ npx @yrest/cli init --file api.yml # custom filename
125
127
  npx @yrest/cli init --sample relational --file api.yml
126
128
  ```
127
129
 
128
- | Flag | Default | Description |
129
- | ---------- | -------- | ----------------------------------- |
130
- | `--file` | `db.yml` | Output filename |
131
- | `--sample` | `basic` | Sample data (`basic`, `relational`) |
130
+ | Flag | Default | Description |
131
+ | ---------- | -------- | ------------------------------------------------ |
132
+ | `--file` | `db.yml` | Output filename |
133
+ | `--sample` | `basic` | Sample data (`basic`, `relational`, `ecommerce`) |
132
134
 
133
135
  **Samples:**
134
136
 
135
- - `basic` — two independent collections: `users` and `products`
136
- - `relational` — three collections with `_rel` relationships: `users`, `posts` and `comments`
137
+ - `basic` — three independent collections: `users`, `products` and `categories`
138
+ - `relational` — blog domain with all three relation types: `users`, `posts`, `comments`, `tags` and a pivot table
139
+ - `ecommerce` — e-commerce domain with products, orders, reviews and custom `_routes` (scenarios, template vars, error injection)
137
140
 
138
141
  ---
139
142
 
@@ -392,29 +395,97 @@ Returns the first 5 posts by user 1, sorted alphabetically by title, with the us
392
395
 
393
396
  ## Relational data
394
397
 
395
- Use `_rel` to declare foreign key relationships between collections:
398
+ Use `_rel` to declare relationships between collections. Three relation types are supported.
399
+
400
+ ### `many2one` (default)
401
+
402
+ A child record holds a foreign key pointing to a parent. The string shorthand implicitly declares `many2one`:
396
403
 
397
404
  ```yaml
398
405
  _rel:
399
406
  posts:
400
- userId: users
407
+ userId: users # shorthand: many2one
408
+ # equivalent to:
409
+ # userId:
410
+ # type: many2one
411
+ # target: users
412
+ ```
401
413
 
402
- users:
403
- - id: 1
404
- name: Ana
414
+ ### `one2one`
405
415
 
406
- posts:
416
+ One child record belongs to exactly one parent, and a parent has at most one child:
417
+
418
+ ```yaml
419
+ _rel:
420
+ profiles:
421
+ userId:
422
+ type: one2one
423
+ target: users
424
+ ```
425
+
426
+ ### `many2many`
427
+
428
+ Two collections are linked through a pivot (join) table:
429
+
430
+ ```yaml
431
+ _rel:
432
+ posts:
433
+ tags:
434
+ type: many2many
435
+ target: tags
436
+ through: post_tags # pivot collection
437
+ foreignKey: postId
438
+ otherKey: tagId
439
+
440
+ post_tags:
407
441
  - id: 1
408
- title: First post
409
- userId: 1
442
+ postId: 1
443
+ tagId: 2
444
+ ```
445
+
446
+ ### Auto-embedding with `nested: true`
447
+
448
+ Add `nested: true` to any relation to embed the related data automatically in every GET response, without needing `?_expand` or `?_embed`:
449
+
450
+ ```yaml
451
+ _rel:
452
+ posts:
453
+ userId:
454
+ type: many2one
455
+ target: users
456
+ nested: true # user object embedded in every /posts response
457
+ tags:
458
+ type: many2many
459
+ target: tags
460
+ through: post_tags
461
+ foreignKey: postId
462
+ otherKey: tagId
463
+ nested: true # tags array embedded in every /posts response
464
+ ```
465
+
466
+ ```
467
+ GET /posts/1 → { id: 1, title: "...", userId: 1, user: { ... }, tags: [...] }
468
+ ```
469
+
470
+ `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.
471
+
472
+ ### What relations enable
473
+
474
+ **Nested routes (many2one / one2one):**
475
+
476
+ ```
477
+ GET /users/1/posts # all posts where userId === 1
478
+ GET /users/1/posts/3 # post 3 only if it belongs to user 1
479
+ GET /users/1/profiles # profile for user 1 (one2one: returns object, not array)
410
480
  ```
411
481
 
412
- This enables:
482
+ **Nested routes (many2many — bidirectional):**
413
483
 
414
- **Nested routes:**
484
+ Both directions are registered automatically from a single declaration:
415
485
 
416
486
  ```
417
- GET /users/1/posts # all posts where userId === 1
487
+ GET /posts/1/tags # all tags linked to post 1 via pivot
488
+ GET /tags/2/posts # all posts linked to tag 2 via pivot (inverse — auto-created)
418
489
  ```
419
490
 
420
491
  **Embed parent with `?_expand`:**
@@ -426,7 +497,9 @@ GET /posts/1?_expand=user → { id: 1, title: "First post", userId: 1, user:
426
497
  **Embed children with `?_embed`:**
427
498
 
428
499
  ```
429
- GET /users/1?_embed=posts → { id: 1, name: "Ana", posts: [{ id: 1, title: "First post", userId: 1 }] }
500
+ GET /users/1?_embed=posts → { id: 1, name: "Ana", posts: [...] }
501
+ GET /posts/1?_embed=tags → { id: 1, title: "...", tags: [...] } # many2many
502
+ GET /users/1?_embed=profiles → { id: 1, name: "Ana", profiles: { ... } } # one2one: object
430
503
  ```
431
504
 
432
505
  ---
@@ -999,6 +1072,9 @@ const server = createYrestServer({
999
1072
  | Per-route delay (`delay:`) | ✅ |
1000
1073
  | Error injection (`error:` in `_routes`) | ✅ |
1001
1074
  | Configurable ID strategy (`idStrategy`) | ✅ |
1075
+ | Explicit relation types (one2one, many2many) | ✅ |
1076
+ | Bidirectional many2many nested routes | ✅ |
1077
+ | Auto-embedding (`nested: true` in `_rel`) | ✅ |
1002
1078
 
1003
1079
  ---
1004
1080