json-rest-api 1.0.10 → 1.0.13
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/.claude/settings.local.json +9 -1
- package/AGENTS.md +34 -0
- package/CLAUDE.md +26 -1
- package/CRUSH.md +55 -0
- package/README.md +3 -3
- package/TODO.todo +8 -0
- package/docs/API.md +17 -18
- package/docs/COMPARISON.md +18 -13
- package/docs/GUIDE/GUIDE_1_Initial_Setup.md +17 -21
- package/docs/GUIDE/GUIDE_2_1_The_Starting_Point.md +33 -2
- package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +61 -7
- package/docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md +58 -61
- package/docs/GUIDE/GUIDE_2_4_HasMany_Records.md +37 -37
- package/docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md +103 -87
- package/docs/GUIDE/GUIDE_2_6_Many_To_Many.md +37 -34
- package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +45 -42
- package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +3 -3
- package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +1 -1
- package/docs/GUIDE/GUIDE_3_Field_Transformations.md +5 -5
- package/docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md +498 -755
- package/docs/GUIDE/GUIDE_6_Positioning.md +1 -3
- package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +4 -5
- package/docs/GUIDE/GUIDE_X_Bulk_Operations.md +15 -6
- package/docs/GUIDE/GUIDE_X_Cors.md +2 -5
- package/docs/GUIDE/GUIDE_X_File_Uploads.md +2 -3
- package/docs/GUIDE/GUIDE_X_Multihome.md +1 -4
- package/docs/GUIDE/GUIDE_X_SocketIO.md +0 -2
- package/docs/GUIDE/GUIDE_X_URL_Management.md +317 -0
- package/docs/GUIDE/index.md +3 -0
- package/docs/QUICKSTART.md +12 -12
- package/docs/index.md +2 -2
- package/docs/mini-guides/file-uploads-complete.md +0 -1
- package/examples/url-override-example.js +137 -0
- package/index.js +7 -1
- package/package.json +7 -11
- package/plugins/core/bulk-operations-plugin.js +2 -0
- package/plugins/core/connectors/express-plugin.js +43 -24
- package/plugins/core/connectors/lib/request-helpers.js +2 -2
- package/plugins/core/lib/anyapi/anyapi-registry.js +821 -0
- package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +647 -0
- package/plugins/core/lib/anyapi/schema-utils.js +186 -0
- package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +113 -0
- package/plugins/core/lib/dbTablesOperations.js +305 -0
- package/plugins/core/lib/querying/knex-cross-table-search.js +64 -108
- package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +21 -27
- package/plugins/core/lib/querying/knex-pagination-helpers.js +36 -20
- package/plugins/core/lib/querying/knex-query-helpers-base.js +49 -14
- package/plugins/core/lib/querying/knex-query-helpers.js +378 -222
- package/plugins/core/lib/querying/knex-relationship-includes.js +262 -87
- package/plugins/core/lib/querying/url-helpers.js +119 -0
- package/plugins/core/lib/querying-writing/compile-schemas.js +36 -28
- package/plugins/core/lib/querying-writing/database-capabilities.js +10 -9
- package/plugins/core/lib/querying-writing/knex-field-helpers.js +9 -3
- package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +2 -2
- package/plugins/core/lib/querying-writing/payload-validators.js +10 -16
- package/plugins/core/lib/querying-writing/schema-helpers.js +52 -60
- package/plugins/core/lib/querying-writing/scope-validations.js +78 -22
- package/plugins/core/lib/querying-writing/simplified-helpers.js +208 -61
- package/plugins/core/lib/storage/storage-adapter.js +160 -0
- package/plugins/core/lib/writing/relationship-processor.js +4 -11
- package/plugins/core/rest-api-access.js +456 -0
- package/plugins/core/rest-api-anyapi-knex-plugin.js +2285 -0
- package/plugins/core/rest-api-knex-plugin.js +226 -87
- package/plugins/core/rest-api-multihome-plugin.js +89 -27
- package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +0 -1
- package/plugins/core/rest-api-plugin-methods/check-permissions.js +1 -5
- package/plugins/core/rest-api-plugin-methods/common.js +46 -10
- package/plugins/core/rest-api-plugin-methods/delete-relationship.js +31 -27
- package/plugins/core/rest-api-plugin-methods/delete.js +1 -4
- package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +13 -1
- package/plugins/core/rest-api-plugin-methods/get-related.js +114 -56
- package/plugins/core/rest-api-plugin-methods/get-relationship.js +4 -3
- package/plugins/core/rest-api-plugin-methods/get.js +2 -5
- package/plugins/core/rest-api-plugin-methods/patch.js +15 -16
- package/plugins/core/rest-api-plugin-methods/post-relationship.js +21 -14
- package/plugins/core/rest-api-plugin-methods/post.js +23 -11
- package/plugins/core/rest-api-plugin-methods/put.js +18 -21
- package/plugins/core/rest-api-plugin-methods/query.js +3 -4
- package/plugins/core/rest-api-plugin-methods/release.js +13 -0
- package/plugins/core/rest-api-plugin.js +3 -4
- package/plugins/core/rest-api-positioning-plugin.js +253 -76
- package/plugins/core/socketio-plugin.js +436 -502
- package/quickTest.js +2 -2
- package/scripts/debug-anyapi-review-filter.mjs +63 -0
- package/scripts/inspect-anyapi-descriptor-country.mjs +19 -0
- package/scripts/inspect-anyapi-descriptor.mjs +19 -0
- package/test-debug.js +59 -0
- package/tests/README.md +7 -1
- package/tests/anyapi-basic.test.js +102 -0
- package/tests/anyapi-cursor-pagination.test.js +144 -0
- package/tests/computed-fields.test.js +27 -12
- package/tests/cors.test.js +5 -6
- package/tests/curl.test.js +1 -1
- package/tests/custom-idproperty-relationships.test.js +37 -29
- package/tests/field-getters.test.js +25 -7
- package/tests/fixtures/api-configs.js +576 -150
- package/tests/helpers/storage-mode.js +65 -0
- package/tests/helpers/test-utils.js +85 -3
- package/tests/include-depth-validation.test.js +35 -1
- package/tests/nested-includes.test.js +130 -103
- package/tests/pagination-cursor-multifield.test.js +17 -13
- package/tests/pagination-enhanced.test.js +19 -24
- package/tests/polymorphic-simple.test.js +9 -9
- package/tests/positioning-advanced.test.js +19 -19
- package/tests/positioning-extended.test.js +25 -25
- package/tests/positioning-multifilter.test.js +62 -62
- package/tests/positioning.test.js +7 -7
- package/tests/queries.test.js +5 -5
- package/tests/query-limits.test.js +77 -92
- package/tests/rest-api-access.test.js +206 -0
- package/tests/searchschema-merge.test.js +182 -0
- package/tests/socketio.test.js +33 -14
- package/tests/virtual-fields.test.js +0 -1
- package/docs/DOCS_ALL.md +0 -21894
- package/plugins/core/jwt-auth-plugin.js +0 -897
- package/plugins/core/lib/jwt-auth-helpers.js +0 -130
- package/tests/jwt-auth.test.js +0 -692
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# 2.6 Many to
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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/
|
|
240
|
+
links: { self: '/api/books/1' }
|
|
238
241
|
},
|
|
239
|
-
links: { self: '/api/
|
|
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/
|
|
316
|
-
related: '/api/
|
|
318
|
+
self: '/api/books/1/relationships/authors',
|
|
319
|
+
related: '/api/books/1/authors'
|
|
317
320
|
}
|
|
318
321
|
}
|
|
319
322
|
},
|
|
320
|
-
links: { self: '/api/
|
|
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/
|
|
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/
|
|
346
|
+
links: { self: '/api/authors/2' }
|
|
344
347
|
}
|
|
345
348
|
],
|
|
346
|
-
links: { self: '/api/
|
|
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/
|
|
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/
|
|
426
|
-
related: '/api/
|
|
428
|
+
self: '/api/books/1/relationships/authors',
|
|
429
|
+
related: '/api/books/1/authors'
|
|
427
430
|
}
|
|
428
431
|
}
|
|
429
432
|
},
|
|
430
|
-
links: { self: '/api/
|
|
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/
|
|
445
|
-
related: '/api/
|
|
447
|
+
self: '/api/books/2/relationships/authors',
|
|
448
|
+
related: '/api/books/2/authors'
|
|
446
449
|
}
|
|
447
450
|
}
|
|
448
451
|
},
|
|
449
|
-
links: { self: '/api/
|
|
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/
|
|
464
|
-
related: '/api/
|
|
466
|
+
self: '/api/books/3/relationships/authors',
|
|
467
|
+
related: '/api/books/3/authors'
|
|
465
468
|
}
|
|
466
469
|
}
|
|
467
470
|
},
|
|
468
|
-
links: { self: '/api/
|
|
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/
|
|
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/
|
|
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/
|
|
506
|
+
links: { self: '/api/authors/3' }
|
|
504
507
|
}
|
|
505
508
|
],
|
|
506
|
-
links: { self: '/api/
|
|
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/
|
|
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/
|
|
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/
|
|
611
|
+
links: { self: '/api/books/2' }
|
|
609
612
|
}
|
|
610
613
|
],
|
|
611
|
-
links: { self: '/api/
|
|
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/
|
|
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/
|
|
643
|
+
links: { self: '/api/authors/1' }
|
|
641
644
|
}
|
|
642
645
|
],
|
|
643
|
-
links: { self: '/api/
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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/
|
|
421
|
-
first: '/api/
|
|
422
|
-
next: '/api/
|
|
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/
|
|
488
|
-
first: '/api/
|
|
489
|
-
next: '/api/
|
|
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/
|
|
591
|
-
first: '/api/
|
|
592
|
-
next: '/api/
|
|
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/
|
|
662
|
-
first: '/api/
|
|
663
|
-
next: '/api/
|
|
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/
|
|
782
|
-
first: '/api/
|
|
783
|
-
last: '/api/
|
|
784
|
-
next: '/api/
|
|
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/
|
|
869
|
-
first: '/api/
|
|
870
|
-
last: '/api/
|
|
871
|
-
next: '/api/
|
|
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
|
-
//
|
|
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/
|
|
970
|
-
first: '/api/
|
|
971
|
-
last: '/api/
|
|
972
|
-
next: '/api/
|
|
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/
|
|
1016
|
-
first: '/api/
|
|
1017
|
-
last: '/api/
|
|
1018
|
-
next: '/api/
|
|
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/
|
|
1064
|
-
"first": "/api/
|
|
1065
|
-
"last": "/api/
|
|
1066
|
-
"prev": "/api/
|
|
1067
|
-
"next": "/api/
|
|
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/
|
|
1218
|
-
first: '/api/
|
|
1219
|
-
last: '/api/
|
|
1220
|
-
next: '/api/
|
|
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
|
|
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
|
-
|
|
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
|
|
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'
|
|
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
|
});
|