json-rest-api 1.0.10 → 1.0.11

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 (117) hide show
  1. package/.claude/settings.local.json +9 -1
  2. package/AGENTS.md +34 -0
  3. package/CLAUDE.md +26 -1
  4. package/CRUSH.md +55 -0
  5. package/README.md +3 -3
  6. package/TODO.todo +8 -0
  7. package/docs/API.md +17 -18
  8. package/docs/COMPARISON.md +18 -13
  9. package/docs/GUIDE/GUIDE_1_Initial_Setup.md +17 -21
  10. package/docs/GUIDE/GUIDE_2_1_The_Starting_Point.md +33 -2
  11. package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +61 -7
  12. package/docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md +58 -61
  13. package/docs/GUIDE/GUIDE_2_4_HasMany_Records.md +37 -37
  14. package/docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md +103 -87
  15. package/docs/GUIDE/GUIDE_2_6_Many_To_Many.md +37 -34
  16. package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +45 -42
  17. package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +3 -3
  18. package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +1 -1
  19. package/docs/GUIDE/GUIDE_3_Field_Transformations.md +5 -5
  20. package/docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md +498 -755
  21. package/docs/GUIDE/GUIDE_6_Positioning.md +1 -3
  22. package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +4 -5
  23. package/docs/GUIDE/GUIDE_X_Bulk_Operations.md +15 -6
  24. package/docs/GUIDE/GUIDE_X_Cors.md +2 -5
  25. package/docs/GUIDE/GUIDE_X_File_Uploads.md +2 -3
  26. package/docs/GUIDE/GUIDE_X_Multihome.md +1 -4
  27. package/docs/GUIDE/GUIDE_X_SocketIO.md +0 -2
  28. package/docs/GUIDE/GUIDE_X_URL_Management.md +317 -0
  29. package/docs/GUIDE/index.md +3 -0
  30. package/docs/QUICKSTART.md +12 -12
  31. package/docs/index.md +2 -2
  32. package/docs/mini-guides/file-uploads-complete.md +0 -1
  33. package/examples/url-override-example.js +137 -0
  34. package/index.js +7 -1
  35. package/package.json +5 -9
  36. package/plugins/core/bulk-operations-plugin.js +2 -0
  37. package/plugins/core/connectors/express-plugin.js +43 -24
  38. package/plugins/core/connectors/lib/request-helpers.js +2 -2
  39. package/plugins/core/lib/anyapi/anyapi-registry.js +821 -0
  40. package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +647 -0
  41. package/plugins/core/lib/anyapi/schema-utils.js +186 -0
  42. package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +113 -0
  43. package/plugins/core/lib/dbTablesOperations.js +305 -0
  44. package/plugins/core/lib/querying/knex-cross-table-search.js +64 -108
  45. package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +21 -27
  46. package/plugins/core/lib/querying/knex-pagination-helpers.js +36 -20
  47. package/plugins/core/lib/querying/knex-query-helpers-base.js +49 -14
  48. package/plugins/core/lib/querying/knex-query-helpers.js +378 -222
  49. package/plugins/core/lib/querying/knex-relationship-includes.js +262 -87
  50. package/plugins/core/lib/querying/url-helpers.js +119 -0
  51. package/plugins/core/lib/querying-writing/compile-schemas.js +36 -28
  52. package/plugins/core/lib/querying-writing/database-capabilities.js +10 -9
  53. package/plugins/core/lib/querying-writing/knex-field-helpers.js +9 -3
  54. package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +2 -2
  55. package/plugins/core/lib/querying-writing/payload-validators.js +10 -16
  56. package/plugins/core/lib/querying-writing/schema-helpers.js +52 -60
  57. package/plugins/core/lib/querying-writing/scope-validations.js +78 -22
  58. package/plugins/core/lib/querying-writing/simplified-helpers.js +208 -61
  59. package/plugins/core/lib/storage/storage-adapter.js +160 -0
  60. package/plugins/core/lib/writing/relationship-processor.js +4 -11
  61. package/plugins/core/rest-api-access.js +456 -0
  62. package/plugins/core/rest-api-anyapi-knex-plugin.js +2285 -0
  63. package/plugins/core/rest-api-knex-plugin.js +226 -87
  64. package/plugins/core/rest-api-multihome-plugin.js +89 -27
  65. package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +0 -1
  66. package/plugins/core/rest-api-plugin-methods/check-permissions.js +1 -5
  67. package/plugins/core/rest-api-plugin-methods/common.js +46 -10
  68. package/plugins/core/rest-api-plugin-methods/delete-relationship.js +31 -27
  69. package/plugins/core/rest-api-plugin-methods/delete.js +1 -4
  70. package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +13 -1
  71. package/plugins/core/rest-api-plugin-methods/get-related.js +114 -56
  72. package/plugins/core/rest-api-plugin-methods/get-relationship.js +4 -3
  73. package/plugins/core/rest-api-plugin-methods/get.js +2 -5
  74. package/plugins/core/rest-api-plugin-methods/patch.js +15 -16
  75. package/plugins/core/rest-api-plugin-methods/post-relationship.js +21 -14
  76. package/plugins/core/rest-api-plugin-methods/post.js +23 -11
  77. package/plugins/core/rest-api-plugin-methods/put.js +18 -21
  78. package/plugins/core/rest-api-plugin-methods/query.js +3 -4
  79. package/plugins/core/rest-api-plugin-methods/release.js +13 -0
  80. package/plugins/core/rest-api-plugin.js +3 -4
  81. package/plugins/core/rest-api-positioning-plugin.js +253 -76
  82. package/plugins/core/socketio-plugin.js +436 -502
  83. package/quickTest.js +2 -2
  84. package/scripts/debug-anyapi-review-filter.mjs +63 -0
  85. package/scripts/inspect-anyapi-descriptor-country.mjs +19 -0
  86. package/scripts/inspect-anyapi-descriptor.mjs +19 -0
  87. package/test-debug.js +59 -0
  88. package/tests/README.md +7 -1
  89. package/tests/anyapi-basic.test.js +102 -0
  90. package/tests/anyapi-cursor-pagination.test.js +144 -0
  91. package/tests/computed-fields.test.js +27 -12
  92. package/tests/cors.test.js +5 -6
  93. package/tests/curl.test.js +1 -1
  94. package/tests/custom-idproperty-relationships.test.js +37 -29
  95. package/tests/field-getters.test.js +25 -7
  96. package/tests/fixtures/api-configs.js +576 -150
  97. package/tests/helpers/storage-mode.js +65 -0
  98. package/tests/helpers/test-utils.js +85 -3
  99. package/tests/include-depth-validation.test.js +35 -1
  100. package/tests/nested-includes.test.js +130 -103
  101. package/tests/pagination-cursor-multifield.test.js +17 -13
  102. package/tests/pagination-enhanced.test.js +19 -24
  103. package/tests/polymorphic-simple.test.js +9 -9
  104. package/tests/positioning-advanced.test.js +19 -19
  105. package/tests/positioning-extended.test.js +25 -25
  106. package/tests/positioning-multifilter.test.js +62 -62
  107. package/tests/positioning.test.js +7 -7
  108. package/tests/queries.test.js +5 -5
  109. package/tests/query-limits.test.js +77 -92
  110. package/tests/rest-api-access.test.js +206 -0
  111. package/tests/searchschema-merge.test.js +182 -0
  112. package/tests/socketio.test.js +33 -14
  113. package/tests/virtual-fields.test.js +0 -1
  114. package/docs/DOCS_ALL.md +0 -21894
  115. package/plugins/core/jwt-auth-plugin.js +0 -897
  116. package/plugins/core/lib/jwt-auth-helpers.js +0 -130
  117. package/tests/jwt-auth.test.js +0 -692
@@ -1,4 +1,4 @@
1
- # 2.6 Many to many (hasMany with through records)
1
+ # 2.6 Many to Many Relationships
2
2
 
3
3
  Many-to-many relationships connect two resources through a pivot/junction table. For example, books can have multiple authors, and authors can write multiple books. This relationship is managed through a `book_authors` table that stores the connections.
4
4
 
@@ -16,7 +16,7 @@ await api.addResource('books', {
16
16
  },
17
17
  relationships: {
18
18
  authors: {
19
- hasMany: 'authors',
19
+ type: 'manyToMany',
20
20
  through: 'book_authors', // The pivot table
21
21
  foreignKey: 'book_id', // Column in pivot table pointing to books
22
22
  otherKey: 'author_id' // Column in pivot table pointing to authors
@@ -44,7 +44,7 @@ await api.addResource('authors', {
44
44
  },
45
45
  relationships: {
46
46
  books: {
47
- hasMany: 'books',
47
+ type: 'manyToMany',
48
48
  through: 'book_authors', // Same pivot table
49
49
  foreignKey: 'author_id', // Column in pivot table pointing to authors
50
50
  otherKey: 'book_id' // Column in pivot table pointing to books
@@ -79,7 +79,10 @@ await api.addResource('book_authors', {
79
79
  await api.resources.book_authors.createKnexTable();
80
80
  ```
81
81
 
82
- The key difference from regular hasMany relationships is the `through` property, which specifies the pivot table. Both `foreignKey` and `otherKey` are mandatory for many-to-many relationships.
82
+ The `manyToMany` relationship type requires the following properties:
83
+ - `through`: The pivot table name
84
+ - `foreignKey`: The column in the pivot table pointing to the current resource
85
+ - `otherKey`: The column in the pivot table pointing to the related resource
83
86
 
84
87
  Now let's create some data and explore how to work with many-to-many relationships:
85
88
 
@@ -234,9 +237,9 @@ Book without includes (non-simplified):
234
237
  ]
235
238
  }
236
239
  },
237
- links: { self: '/api/1.0/books/1' }
240
+ links: { self: '/api/books/1' }
238
241
  },
239
- links: { self: '/api/1.0/books/1' }
242
+ links: { self: '/api/books/1' }
240
243
  }
241
244
  ```
242
245
 
@@ -312,12 +315,12 @@ Book with authors (non-simplified): {
312
315
  authors: {
313
316
  data: [ { type: 'authors', id: '1' }, { type: 'authors', id: '2' } ],
314
317
  links: {
315
- self: '/api/1.0/books/1/relationships/authors',
316
- related: '/api/1.0/books/1/authors'
318
+ self: '/api/books/1/relationships/authors',
319
+ related: '/api/books/1/authors'
317
320
  }
318
321
  }
319
322
  },
320
- links: { self: '/api/1.0/books/1' }
323
+ links: { self: '/api/books/1' }
321
324
  },
322
325
  included: [
323
326
  {
@@ -329,7 +332,7 @@ Book with authors (non-simplified): {
329
332
  bio: 'British author of fiction, horror, and fantasy'
330
333
  },
331
334
  relationships: { books: { data: [] } },
332
- links: { self: '/api/1.0/authors/1' }
335
+ links: { self: '/api/authors/1' }
333
336
  },
334
337
  {
335
338
  type: 'authors',
@@ -340,10 +343,10 @@ Book with authors (non-simplified): {
340
343
  bio: 'English humorist and fantasy author'
341
344
  },
342
345
  relationships: { books: { data: [] } },
343
- links: { self: '/api/1.0/authors/2' }
346
+ links: { self: '/api/authors/2' }
344
347
  }
345
348
  ],
346
- links: { self: '/api/1.0/books/1' }
349
+ links: { self: '/api/books/1' }
347
350
  }
348
351
  Books with authors (simplified): {
349
352
  data: [
@@ -403,7 +406,7 @@ Books with authors (simplified): {
403
406
  ]
404
407
  }
405
408
  ],
406
- links: { self: '/api/1.0/books?include=authors' }
409
+ links: { self: '/api/books?include=authors' }
407
410
  }
408
411
  Books with authors (non-simplified): {
409
412
  data: [
@@ -422,12 +425,12 @@ Books with authors (non-simplified): {
422
425
  { type: 'authors', id: '2' }
423
426
  ],
424
427
  links: {
425
- self: '/api/1.0/books/1/relationships/authors',
426
- related: '/api/1.0/books/1/authors'
428
+ self: '/api/books/1/relationships/authors',
429
+ related: '/api/books/1/authors'
427
430
  }
428
431
  }
429
432
  },
430
- links: { self: '/api/1.0/books/1' }
433
+ links: { self: '/api/books/1' }
431
434
  },
432
435
  {
433
436
  type: 'books',
@@ -441,12 +444,12 @@ Books with authors (non-simplified): {
441
444
  authors: {
442
445
  data: [ { type: 'authors', id: '1' } ],
443
446
  links: {
444
- self: '/api/1.0/books/2/relationships/authors',
445
- related: '/api/1.0/books/2/authors'
447
+ self: '/api/books/2/relationships/authors',
448
+ related: '/api/books/2/authors'
446
449
  }
447
450
  }
448
451
  },
449
- links: { self: '/api/1.0/books/2' }
452
+ links: { self: '/api/books/2' }
450
453
  },
451
454
  {
452
455
  type: 'books',
@@ -460,12 +463,12 @@ Books with authors (non-simplified): {
460
463
  authors: {
461
464
  data: [ { type: 'authors', id: '3' } ],
462
465
  links: {
463
- self: '/api/1.0/books/3/relationships/authors',
464
- related: '/api/1.0/books/3/authors'
466
+ self: '/api/books/3/relationships/authors',
467
+ related: '/api/books/3/authors'
465
468
  }
466
469
  }
467
470
  },
468
- links: { self: '/api/1.0/books/3' }
471
+ links: { self: '/api/books/3' }
469
472
  }
470
473
  ],
471
474
  included: [
@@ -478,7 +481,7 @@ Books with authors (non-simplified): {
478
481
  bio: 'British author of fiction, horror, and fantasy'
479
482
  },
480
483
  relationships: { books: { data: [] } },
481
- links: { self: '/api/1.0/authors/1' }
484
+ links: { self: '/api/authors/1' }
482
485
  },
483
486
  {
484
487
  type: 'authors',
@@ -489,7 +492,7 @@ Books with authors (non-simplified): {
489
492
  bio: 'English humorist and fantasy author'
490
493
  },
491
494
  relationships: { books: { data: [] } },
492
- links: { self: '/api/1.0/authors/2' }
495
+ links: { self: '/api/authors/2' }
493
496
  },
494
497
  {
495
498
  type: 'authors',
@@ -500,10 +503,10 @@ Books with authors (non-simplified): {
500
503
  bio: 'American author of horror and supernatural fiction'
501
504
  },
502
505
  relationships: { books: { data: [] } },
503
- links: { self: '/api/1.0/authors/3' }
506
+ links: { self: '/api/authors/3' }
504
507
  }
505
508
  ],
506
- links: { self: '/api/1.0/books?include=authors' }
509
+ links: { self: '/api/books?include=authors' }
507
510
  }
508
511
 
509
512
  ```
@@ -572,7 +575,7 @@ Books by Neil (simplified): {
572
575
  authors_ids: [ '1' ]
573
576
  }
574
577
  ],
575
- links: { self: '/api/1.0/books?filters[authorName]=Neil' }
578
+ links: { self: '/api/books?filters[authorName]=Neil' }
576
579
  }
577
580
  Books by Neil (non-simplified): {
578
581
  data: [
@@ -592,7 +595,7 @@ Books by Neil (non-simplified): {
592
595
  ]
593
596
  }
594
597
  },
595
- links: { self: '/api/1.0/books/1' }
598
+ links: { self: '/api/books/1' }
596
599
  },
597
600
  {
598
601
  type: 'books',
@@ -605,10 +608,10 @@ Books by Neil (non-simplified): {
605
608
  relationships: {
606
609
  authors: { data: [ { type: 'authors', id: '1' } ] }
607
610
  },
608
- links: { self: '/api/1.0/books/2' }
611
+ links: { self: '/api/books/2' }
609
612
  }
610
613
  ],
611
- links: { self: '/api/1.0/books?filters[authorName]=Neil' }
614
+ links: { self: '/api/books?filters[authorName]=Neil' }
612
615
  }
613
616
  Authors who wrote books with "Gods" (simplified): {
614
617
  data: [
@@ -620,7 +623,7 @@ Authors who wrote books with "Gods" (simplified): {
620
623
  books_ids: [ '1', '2' ]
621
624
  }
622
625
  ],
623
- links: { self: '/api/1.0/authors?filters[bookTitle]=Gods' }
626
+ links: { self: '/api/authors?filters[bookTitle]=Gods' }
624
627
  }
625
628
  Authors who wrote books with "Gods" (non-simplified): {
626
629
  data: [
@@ -637,10 +640,10 @@ Authors who wrote books with "Gods" (non-simplified): {
637
640
  data: [ { type: 'books', id: '1' }, { type: 'books', id: '2' } ]
638
641
  }
639
642
  },
640
- links: { self: '/api/1.0/authors/1' }
643
+ links: { self: '/api/authors/1' }
641
644
  }
642
645
  ],
643
- links: { self: '/api/1.0/authors?filters[bookTitle]=Gods' }
646
+ links: { self: '/api/authors?filters[bookTitle]=Gods' }
644
647
  }
645
648
  Neil books with full author data: {
646
649
  data: [
@@ -684,7 +687,7 @@ Neil books with full author data: {
684
687
  ]
685
688
  }
686
689
  ],
687
- links: { self: '/api/1.0/books?filters[authorName]=Neil&include=authors' }
690
+ links: { self: '/api/books?filters[authorName]=Neil&include=authors' }
688
691
  }
689
692
  ```
690
693
 
@@ -15,7 +15,8 @@ await api.addResource('posts', {
15
15
  },
16
16
  relationships: {
17
17
  comments: {
18
- hasMany: 'comments',
18
+ type: 'hasMany',
19
+ target: 'comments',
19
20
  foreignKey: 'post_id',
20
21
  include: {
21
22
  limit: 5, // Default limit for included comments
@@ -23,7 +24,7 @@ await api.addResource('posts', {
23
24
  }
24
25
  },
25
26
  tags: {
26
- hasMany: 'tags',
27
+ type: 'manyToMany',
27
28
  through: 'post_tags',
28
29
  foreignKey: 'post_id',
29
30
  otherKey: 'tag_id',
@@ -69,7 +70,7 @@ await api.addResource('tags', {
69
70
  },
70
71
  relationships: {
71
72
  posts: {
72
- hasMany: 'posts',
73
+ type: 'manyToMany',
73
74
  through: 'post_tags',
74
75
  foreignKey: 'tag_id',
75
76
  otherKey: 'post_id'
@@ -417,9 +418,9 @@ First page: {
417
418
  }
418
419
  },
419
420
  links: {
420
- self: '/api/1.0/posts?sort=-created_at&&&page[size]=5',
421
- first: '/api/1.0/posts?sort=-created_at&&&page[size]=5',
422
- next: '/api/1.0/posts?sort=-created_at&&&page[size]=5&page[after]=created_at:1753675599811'
421
+ self: '/api/posts?sort=-created_at&&&page[size]=5',
422
+ first: '/api/posts?sort=-created_at&&&page[size]=5',
423
+ next: '/api/posts?sort=-created_at&&&page[size]=5&page[after]=created_at:1753675599811'
423
424
  }
424
425
  }
425
426
 
@@ -484,9 +485,9 @@ Second page: {
484
485
  }
485
486
  },
486
487
  links: {
487
- self: '/api/1.0/posts?sort=-created_at&&&page[size]=5&page[after]=created_at:1753675599811',
488
- first: '/api/1.0/posts?sort=-created_at&&&page[size]=5',
489
- next: '/api/1.0/posts?sort=-created_at&&&page[size]=5&page[after]=created_at:1753675599797'
488
+ self: '/api/posts?sort=-created_at&&&page[size]=5&page[after]=created_at:1753675599811',
489
+ first: '/api/posts?sort=-created_at&&&page[size]=5',
490
+ next: '/api/posts?sort=-created_at&&&page[size]=5&page[after]=created_at:1753675599797'
490
491
  }
491
492
  }
492
493
  ```
@@ -587,9 +588,9 @@ First multiField page: {
587
588
  }
588
589
  },
589
590
  links: {
590
- self: '/api/1.0/posts?sort=view_count&sort=-created_at&&&page[size]=5',
591
- first: '/api/1.0/posts?sort=view_count&sort=-created_at&&&page[size]=5',
592
- next: '/api/1.0/posts?sort=view_count&sort=-created_at&&&page[size]=5&page[after]=view_count:74,created_at:1753676192069'
591
+ self: '/api/posts?sort=view_count&sort=-created_at&&&page[size]=5',
592
+ first: '/api/posts?sort=view_count&sort=-created_at&&&page[size]=5',
593
+ next: '/api/posts?sort=view_count&sort=-created_at&&&page[size]=5&page[after]=view_count:74,created_at:1753676192069'
593
594
  }
594
595
  }
595
596
  Next multiField page: {
@@ -658,9 +659,9 @@ Next multiField page: {
658
659
  }
659
660
  },
660
661
  links: {
661
- self: '/api/1.0/posts?sort=view_count&sort=-created_at&&&page[size]=5&page[after]=view_count:74,created_at:1753676192069',
662
- first: '/api/1.0/posts?sort=view_count&sort=-created_at&&&page[size]=5',
663
- next: '/api/1.0/posts?sort=view_count&sort=-created_at&&&page[size]=5&page[after]=view_count:290,created_at:1753676192056'
662
+ self: '/api/posts?sort=view_count&sort=-created_at&&&page[size]=5&page[after]=view_count:74,created_at:1753676192069',
663
+ first: '/api/posts?sort=view_count&sort=-created_at&&&page[size]=5',
664
+ next: '/api/posts?sort=view_count&sort=-created_at&&&page[size]=5&page[after]=view_count:290,created_at:1753676192056'
664
665
  }
665
666
  }
666
667
  ```
@@ -778,10 +779,10 @@ Popular sort: {
778
779
  pagination: { page: 1, pageSize: 5, pageCount: 5, total: 25, hasMore: true }
779
780
  },
780
781
  links: {
781
- self: '/api/1.0/posts?sort=-view_count&&&page[number]=1&page[size]=5',
782
- first: '/api/1.0/posts?sort=-view_count&&&page[number]=1&page[size]=5',
783
- last: '/api/1.0/posts?sort=-view_count&&&page[number]=5&page[size]=5',
784
- next: '/api/1.0/posts?sort=-view_count&&&page[number]=2&page[size]=5'
782
+ self: '/api/posts?sort=-view_count&&&page[number]=1&page[size]=5',
783
+ first: '/api/posts?sort=-view_count&&&page[number]=1&page[size]=5',
784
+ last: '/api/posts?sort=-view_count&&&page[number]=5&page[size]=5',
785
+ next: '/api/posts?sort=-view_count&&&page[number]=2&page[size]=5'
785
786
  }
786
787
  }
787
788
  Multisort: {
@@ -865,10 +866,10 @@ Multisort: {
865
866
  pagination: { page: 1, pageSize: 5, pageCount: 5, total: 25, hasMore: true }
866
867
  },
867
868
  links: {
868
- self: '/api/1.0/posts?sort=published_at&sort=-view_count&&&page[number]=1&page[size]=5',
869
- first: '/api/1.0/posts?sort=published_at&sort=-view_count&&&page[number]=1&page[size]=5',
870
- last: '/api/1.0/posts?sort=published_at&sort=-view_count&&&page[number]=5&page[size]=5',
871
- next: '/api/1.0/posts?sort=published_at&sort=-view_count&&&page[number]=2&page[size]=5'
869
+ self: '/api/posts?sort=published_at&sort=-view_count&&&page[number]=1&page[size]=5',
870
+ first: '/api/posts?sort=published_at&sort=-view_count&&&page[number]=1&page[size]=5',
871
+ last: '/api/posts?sort=published_at&sort=-view_count&&&page[number]=5&page[size]=5',
872
+ next: '/api/posts?sort=published_at&sort=-view_count&&&page[number]=2&page[size]=5'
872
873
  }
873
874
  }
874
875
  ```
@@ -881,7 +882,9 @@ When you include relationships in your query, they use the default `limit` and `
881
882
  // The relationship was defined with default limits:
882
883
  // relationships: {
883
884
  // comments: {
884
- // hasMany: 'comments',
885
+ // type: 'hasMany',
886
+ // target: 'comments',
887
+ // foreignKey: 'post_id',
885
888
  // include: {
886
889
  // limit: 5, // Always return max 5 comments
887
890
  // orderBy: ['-created_at'] // Always order by newest first
@@ -966,10 +969,10 @@ Posts with comments: {
966
969
  pagination: { page: 1, pageSize: 3, pageCount: 9, total: 25, hasMore: true }
967
970
  },
968
971
  links: {
969
- self: '/api/1.0/posts?include=comments&&sort=-created_at&page[number]=1&page[size]=3',
970
- first: '/api/1.0/posts?include=comments&&sort=-created_at&page[number]=1&page[size]=3',
971
- last: '/api/1.0/posts?include=comments&&sort=-created_at&page[number]=9&page[size]=3',
972
- next: '/api/1.0/posts?include=comments&&sort=-created_at&page[number]=2&page[size]=3'
972
+ self: '/api/posts?include=comments&&sort=-created_at&page[number]=1&page[size]=3',
973
+ first: '/api/posts?include=comments&&sort=-created_at&page[number]=1&page[size]=3',
974
+ last: '/api/posts?include=comments&&sort=-created_at&page[number]=9&page[size]=3',
975
+ next: '/api/posts?include=comments&&sort=-created_at&page[number]=2&page[size]=3'
973
976
  }
974
977
  }
975
978
 
@@ -1012,10 +1015,10 @@ Posts with all relationships: {
1012
1015
  pagination: { page: 1, pageSize: 2, pageCount: 13, total: 25, hasMore: true }
1013
1016
  },
1014
1017
  links: {
1015
- self: '/api/1.0/posts?include=comments&include=tags&&sort=-created_at&page[number]=1&page[size]=2',
1016
- first: '/api/1.0/posts?include=comments&include=tags&&sort=-created_at&page[number]=1&page[size]=2',
1017
- last: '/api/1.0/posts?include=comments&include=tags&&sort=-created_at&page[number]=13&page[size]=2',
1018
- next: '/api/1.0/posts?include=comments&include=tags&&sort=-created_at&page[number]=2&page[size]=2'
1018
+ self: '/api/posts?include=comments&include=tags&&sort=-created_at&page[number]=1&page[size]=2',
1019
+ first: '/api/posts?include=comments&include=tags&&sort=-created_at&page[number]=1&page[size]=2',
1020
+ last: '/api/posts?include=comments&include=tags&&sort=-created_at&page[number]=13&page[size]=2',
1021
+ next: '/api/posts?include=comments&include=tags&&sort=-created_at&page[number]=2&page[size]=2'
1019
1022
  }
1020
1023
  }
1021
1024
  ```
@@ -1060,11 +1063,11 @@ console.log('Pagination meta:', JSON.stringify(pagedResult.meta, null, 2));
1060
1063
 
1061
1064
  ```text
1062
1065
  Pagination links: {
1063
- "self": "/api/1.0/posts?sort=-published_at&&&page[number]=2&page[size]=5",
1064
- "first": "/api/1.0/posts?sort=-published_at&&&page[number]=1&page[size]=5",
1065
- "last": "/api/1.0/posts?sort=-published_at&&&page[number]=5&page[size]=5",
1066
- "prev": "/api/1.0/posts?sort=-published_at&&&page[number]=1&page[size]=5",
1067
- "next": "/api/1.0/posts?sort=-published_at&&&page[number]=3&page[size]=5"
1066
+ "self": "/api/posts?sort=-published_at&&&page[number]=2&page[size]=5",
1067
+ "first": "/api/posts?sort=-published_at&&&page[number]=1&page[size]=5",
1068
+ "last": "/api/posts?sort=-published_at&&&page[number]=5&page[size]=5",
1069
+ "prev": "/api/posts?sort=-published_at&&&page[number]=1&page[size]=5",
1070
+ "next": "/api/posts?sort=-published_at&&&page[number]=3&page[size]=5"
1068
1071
  }
1069
1072
  Pagination meta: {
1070
1073
  "pagination": {
@@ -1214,10 +1217,10 @@ Complex query results: {
1214
1217
  pagination: { page: 1, pageSize: 3, pageCount: 4, total: 12, hasMore: true }
1215
1218
  },
1216
1219
  links: {
1217
- self: '/api/1.0/posts?filters[title]=Even&sort=-view_count&sort=published_at&include=comments&include=tags&&page[number]=1&page[size]=3',
1218
- first: '/api/1.0/posts?filters[title]=Even&sort=-view_count&sort=published_at&include=comments&include=tags&&page[number]=1&page[size]=3',
1219
- last: '/api/1.0/posts?filters[title]=Even&sort=-view_count&sort=published_at&include=comments&include=tags&&page[number]=4&page[size]=3',
1220
- next: '/api/1.0/posts?filters[title]=Even&sort=-view_count&sort=published_at&include=comments&include=tags&&page[number]=2&page[size]=3'
1220
+ self: '/api/posts?filters[title]=Even&sort=-view_count&sort=published_at&include=comments&include=tags&&page[number]=1&page[size]=3',
1221
+ first: '/api/posts?filters[title]=Even&sort=-view_count&sort=published_at&include=comments&include=tags&&page[number]=1&page[size]=3',
1222
+ last: '/api/posts?filters[title]=Even&sort=-view_count&sort=published_at&include=comments&include=tags&&page[number]=4&page[size]=3',
1223
+ next: '/api/posts?filters[title]=Even&sort=-view_count&sort=published_at&include=comments&include=tags&&page[number]=2&page[size]=3'
1221
1224
  }
1222
1225
  }
1223
1226
  ```
@@ -1230,4 +1233,4 @@ The pagination and ordering system is designed to be efficient and predictable:
1230
1233
 
1231
1234
  ---
1232
1235
 
1233
- [Previous: 2.6 Many to many (hasMany with through records)](./GUIDE_2_6_Many_To_Many.md) | [Back to Guide](./README.md)
1236
+ [Previous: 2.6 Many to Many Relationships](./GUIDE_2_6_Many_To_Many.md) | [Back to Guide](./README.md)
@@ -14,7 +14,7 @@ await api.addResource('publishers', {
14
14
  country: { type: 'string', required: true, max: 100 }
15
15
  },
16
16
  relationships: {
17
- books: { hasMany: 'books', foreignKey: 'publisher_id' }
17
+ books: { type: 'hasMany', target: 'books', foreignKey: 'publisher_id' }
18
18
  }
19
19
  });
20
20
  await api.resources.publishers.createKnexTable();
@@ -27,7 +27,7 @@ await api.addResource('authors', {
27
27
  birth_year: { type: 'number', required: true }
28
28
  },
29
29
  relationships: {
30
- books: { hasMany: 'books', foreignKey: 'author_id' }
30
+ books: { type: 'hasMany', target: 'books', foreignKey: 'author_id' }
31
31
  }
32
32
  });
33
33
  await api.resources.authors.createKnexTable();
@@ -53,7 +53,7 @@ await api.addResource('books', {
53
53
  relationships: {
54
54
  // Many-to-many relationship with genres
55
55
  genres: {
56
- hasMany: 'genres',
56
+ type: 'manyToMany',
57
57
  through: 'book_genres',
58
58
  foreignKey: 'book_id',
59
59
  otherKey: 'genre_id'
@@ -35,7 +35,7 @@ This guide has been split into the following sections for easier navigation:
35
35
  - Forward and reverse polymorphic search
36
36
  - Complex polymorphic filtering
37
37
 
38
- ## [2.6 Many to many (hasMany with through records)](./GUIDE_2_6_Many_To_Many.md)
38
+ ## [2.6 Many to Many Relationships](./GUIDE_2_6_Many_To_Many.md)
39
39
  - Understanding many-to-many relationships
40
40
  - Working with pivot tables directly
41
41
  - Including many-to-many records
@@ -624,7 +624,7 @@ await api.addResource('products', {
624
624
  }
625
625
  },
626
626
  relationships: {
627
- reviews: { hasMany: 'reviews', foreignKey: 'product_id' }
627
+ reviews: { type: 'hasMany', target: 'reviews', foreignKey: 'product_id' }
628
628
  }
629
629
  });
630
630
  await api.resources.products.createKnexTable();
@@ -937,7 +937,7 @@ const db = knex({
937
937
  });
938
938
 
939
939
  // Create API instance
940
- const api = new Api({ name: 'blog-api', version: '1.0.0' });
940
+ const api = new Api({ name: 'blog-api' });
941
941
 
942
942
  // Install plugins
943
943
  await api.use(RestApiPlugin);
@@ -952,7 +952,7 @@ await api.addResource('authors', {
952
952
  bio: { type: 'string', nullable: true }
953
953
  },
954
954
  relationships: {
955
- posts: { hasMany: 'posts', foreignKey: 'author_id' }
955
+ posts: { type: 'hasMany', target: 'posts', foreignKey: 'author_id' }
956
956
  },
957
957
  tableName: 'authors'
958
958
  });
@@ -1032,7 +1032,7 @@ await api.addResource('authors', {
1032
1032
  }
1033
1033
  },
1034
1034
  relationships: {
1035
- posts: { hasMany: 'posts', foreignKey: 'author_id' }
1035
+ posts: { type: 'hasMany', target: 'posts', foreignKey: 'author_id' }
1036
1036
  },
1037
1037
  tableName: 'authors'
1038
1038
  });
@@ -1134,7 +1134,7 @@ await api.addResource('authors', {
1134
1134
  }
1135
1135
  },
1136
1136
  relationships: {
1137
- posts: { hasMany: 'posts', foreignKey: 'author_id' }
1137
+ posts: { type: 'hasMany', target: 'posts', foreignKey: 'author_id' }
1138
1138
  },
1139
1139
  tableName: 'authors'
1140
1140
  });