json-rest-api 1.0.22 → 1.0.24

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 (290) hide show
  1. package/CLAUDE.md +1 -2
  2. package/JSKIT_BACKEND_MIGRATION_TRACKER.txt +180 -0
  3. package/README.md +22 -1
  4. package/SINGLE_VALIDATION_CONTRACT_TODO.txt +172 -0
  5. package/docs/API.md +196 -38
  6. package/docs/COMPARISON.md +2 -2
  7. package/docs/GUIDE/GUIDE_1_Initial_Setup.md +111 -5
  8. package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +5 -1
  9. package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +3 -1
  10. package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +12 -60
  11. package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +2 -2
  12. package/docs/GUIDE/GUIDE_3_Field_Transformations.md +5 -1
  13. package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +2 -0
  14. package/docs/GUIDE/GUIDE_X_Autofiltering.md +355 -0
  15. package/docs/GUIDE/GUIDE_X_Fastify.md +78 -0
  16. package/docs/GUIDE/GUIDE_X_Knex_Schema_And_Migrations.md +370 -0
  17. package/docs/GUIDE/GUIDE_X_Query_Projections.md +159 -0
  18. package/docs/GUIDE/GUIDE_X_Writing_Plugins.md +199 -0
  19. package/docs/GUIDE/index.md +18 -9
  20. package/docs/QUICKSTART.md +5 -1
  21. package/docs/index.md +1 -1
  22. package/index.js +3 -2
  23. package/lib/enhanced-logger.js +1 -1
  24. package/package.json +6 -2
  25. package/plugins/core/bulk-operations-plugin.js +26 -34
  26. package/plugins/core/connectors/express-plugin.js +97 -190
  27. package/plugins/core/connectors/fastify-plugin.js +206 -0
  28. package/plugins/core/connectors/lib/busboy-detector.js +4 -4
  29. package/plugins/core/connectors/lib/connector-core.js +195 -0
  30. package/plugins/core/connectors/lib/transport-http-helpers.js +122 -0
  31. package/plugins/core/connectors/lib/transport-route-schemas.js +92 -0
  32. package/plugins/core/lib/anyapi/schema-utils.js +43 -4
  33. package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +4 -1
  34. package/plugins/core/lib/dbIntrospection.js +1078 -0
  35. package/plugins/core/lib/dbTablesOperations.js +1111 -160
  36. package/plugins/core/lib/querying/knex-cross-table-search.js +0 -7
  37. package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +5 -3
  38. package/plugins/core/lib/querying/knex-process-includes.js +3 -4
  39. package/plugins/core/lib/querying/knex-query-helpers.js +21 -76
  40. package/plugins/core/lib/querying/knex-relationship-includes.js +468 -215
  41. package/plugins/core/lib/querying/query-builder-utils.js +19 -0
  42. package/plugins/core/lib/querying/query-field-sort-helpers.js +135 -0
  43. package/plugins/core/lib/querying/sort-helpers.js +33 -0
  44. package/plugins/core/lib/querying/storage-adapter-utils.js +51 -0
  45. package/plugins/core/lib/querying/url-helpers.js +8 -2
  46. package/plugins/core/lib/querying-writing/compile-schemas.js +20 -5
  47. package/plugins/core/lib/querying-writing/knex-field-helpers.js +83 -14
  48. package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +13 -12
  49. package/plugins/core/lib/querying-writing/query-field-helpers.js +88 -0
  50. package/plugins/core/lib/querying-writing/request-contracts.js +590 -0
  51. package/plugins/core/lib/querying-writing/resource-id-normalization.js +131 -0
  52. package/plugins/core/lib/querying-writing/schema-helpers.js +0 -2
  53. package/plugins/core/lib/querying-writing/simplified-helpers.js +4 -11
  54. package/plugins/core/lib/storage/canonical-storage-mapping.js +133 -0
  55. package/plugins/core/lib/storage/storage-adapter.js +82 -4
  56. package/plugins/core/lib/storage/storage-mapping.js +274 -0
  57. package/plugins/core/lib/writing/many-to-many-manipulations.js +4 -2
  58. package/plugins/core/rest-api-anyapi-knex-plugin.js +531 -312
  59. package/plugins/core/rest-api-autofilter-plugin.js +414 -0
  60. package/plugins/core/rest-api-knex-plugin.js +271 -226
  61. package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +10 -5
  62. package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +12 -6
  63. package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +1 -0
  64. package/plugins/core/rest-api-plugin-methods/apply-query-filters.js +41 -0
  65. package/plugins/core/rest-api-plugin-methods/common.js +148 -92
  66. package/plugins/core/rest-api-plugin-methods/delete-relationship.js +11 -2
  67. package/plugins/core/rest-api-plugin-methods/delete.js +6 -1
  68. package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +6 -1
  69. package/plugins/core/rest-api-plugin-methods/get-related.js +7 -2
  70. package/plugins/core/rest-api-plugin-methods/get-relationship.js +7 -2
  71. package/plugins/core/rest-api-plugin-methods/get.js +29 -14
  72. package/plugins/core/rest-api-plugin-methods/patch-relationship.js +10 -3
  73. package/plugins/core/rest-api-plugin-methods/patch.js +56 -12
  74. package/plugins/core/rest-api-plugin-methods/post-relationship.js +11 -2
  75. package/plugins/core/rest-api-plugin-methods/post.js +21 -8
  76. package/plugins/core/rest-api-plugin-methods/put.js +73 -21
  77. package/plugins/core/rest-api-plugin-methods/query.js +15 -53
  78. package/plugins/core/rest-api-plugin-methods/release.js +0 -2
  79. package/plugins/core/rest-api-plugin.js +6 -0
  80. package/plugins/core/rest-api-positioning-plugin.js +2 -5
  81. package/plugins/core/rest-api-query-projections-plugin.js +130 -0
  82. package/plugins/core/socketio-plugin.js +1 -4
  83. package/plugins/storage/local-storage.js +1 -1
  84. package/plugins/storage/s3-storage.js +5 -2
  85. package/quickTest.js +1 -0
  86. package/scripts/run-docs.js +80 -84
  87. package/test-debug.js +1 -1
  88. package/tests/README.md +7 -0
  89. package/tests/anyapi-basic.test.js +3 -0
  90. package/tests/autofilter.test.js +463 -0
  91. package/tests/custom-id-mysql-returning-fallback.test.js +128 -0
  92. package/tests/custom-id-simplified-post.test.js +75 -0
  93. package/tests/custom-idproperty-relationships.test.js +33 -0
  94. package/tests/db-introspection.test.js +521 -0
  95. package/tests/db-table-operations.test.js +786 -0
  96. package/tests/fastify-plugin.test.js +156 -0
  97. package/tests/field-getters.test.js +1 -2
  98. package/tests/field-setters.test.js +0 -4
  99. package/tests/fixtures/api-configs.js +278 -176
  100. package/tests/helpers/fake-fastify.js +99 -0
  101. package/tests/http-connectors-parity.test.js +235 -0
  102. package/tests/id-normalization.test.js +444 -0
  103. package/tests/logical-id-write-validation.test.js +151 -0
  104. package/tests/pagination-cursor-multifield.test.js +11 -11
  105. package/tests/projected-fields.test.js +319 -0
  106. package/tests/relationships.test.js +3 -3
  107. package/tests/searchschema-knex-behavior.test.js +187 -0
  108. package/tests/searchschema-merge.test.js +21 -69
  109. package/tests/storage-mapping.test.js +259 -0
  110. package/audit/00-charter.md +0 -103
  111. package/audit/01-scope-map.md +0 -84
  112. package/audit/02-coverage-matrix.csv +0 -174
  113. package/audit/03-invariants.md +0 -123
  114. package/audit/04-standards-rubric.md +0 -56
  115. package/audit/05-competitor-rubric.md +0 -77
  116. package/audit/06-findings.md +0 -160
  117. package/audit/07-risk-register.csv +0 -10
  118. package/audit/08-remediation-roadmap.md +0 -177
  119. package/audit/09-signoff-check.md +0 -23
  120. package/audit/10-final-synthesis.md +0 -29
  121. package/audit/evidence/code/all-files-json-rest-api.txt +0 -168
  122. package/audit/evidence/code/all-files-json-rest-schema.txt +0 -12
  123. package/audit/evidence/code/boundary-leak-checks.txt +0 -72
  124. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__bulk-operations-plugin.js.txt +0 -480
  125. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__express-plugin.js.txt +0 -532
  126. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__busboy-detector.js.txt +0 -175
  127. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__formidable-detector.js.txt +0 -138
  128. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__request-helpers.js.txt +0 -222
  129. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__file-handling-plugin.js.txt +0 -342
  130. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__dbTablesOperations.js.txt +0 -304
  131. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__fractional-positioning.js.txt +0 -188
  132. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__compile-schemas.js.txt +0 -336
  133. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__connectors-query-parser.js.txt +0 -165
  134. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__database-capabilities.js.txt +0 -265
  135. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__database-value-normalizers.js.txt +0 -369
  136. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__default-data-helpers.js.txt +0 -72
  137. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__field-utils.js.txt +0 -139
  138. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-constants.js.txt +0 -45
  139. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-field-helpers.js.txt +0 -396
  140. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-json-api-transformers.js.txt +0 -162
  141. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__payload-validators.js.txt +0 -1314
  142. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__schema-helpers.js.txt +0 -488
  143. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__scope-validations.js.txt +0 -484
  144. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__simplified-helpers.js.txt +0 -857
  145. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-cross-table-search.js.txt +0 -565
  146. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-json-api-transformers-querying.js.txt +0 -485
  147. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-pagination-helpers.js.txt +0 -560
  148. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-process-includes.js.txt +0 -167
  149. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-query-helpers-base.js.txt +0 -109
  150. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-query-helpers.js.txt +0 -1064
  151. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-relationship-includes.js.txt +0 -1983
  152. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-window-queries.js.txt +0 -328
  153. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__url-helpers.js.txt +0 -119
  154. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__storage__storage-adapter.js.txt +0 -160
  155. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__writing__many-to-many-manipulations.js.txt +0 -300
  156. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__writing__relationship-processor.js.txt +0 -245
  157. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-access.js.txt +0 -459
  158. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-cors-plugin.js.txt +0 -237
  159. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-knex-plugin.js.txt +0 -1135
  160. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-multihome-plugin.js.txt +0 -314
  161. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__compile-resource-schemas.js.txt +0 -7
  162. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__register-relationship-routes.js.txt +0 -71
  163. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__register-scope-routes.js.txt +0 -102
  164. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__turn-scope-init-into-vars.js.txt +0 -49
  165. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__validate-include-configurations.js.txt +0 -39
  166. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__add-route.js.txt +0 -23
  167. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__check-permissions.js.txt +0 -13
  168. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__common.js.txt +0 -639
  169. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__delete-relationship.js.txt +0 -117
  170. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__delete.js.txt +0 -104
  171. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__enrich-attributes.js.txt +0 -199
  172. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get-related.js.txt +0 -353
  173. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get-relationship.js.txt +0 -61
  174. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get.js.txt +0 -184
  175. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__patch-relationship.js.txt +0 -55
  176. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__patch.js.txt +0 -191
  177. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__post-relationship.js.txt +0 -110
  178. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__post.js.txt +0 -168
  179. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__put.js.txt +0 -253
  180. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__query.js.txt +0 -207
  181. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__release.js.txt +0 -12
  182. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin.js.txt +0 -165
  183. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-positioning-plugin.js.txt +0 -668
  184. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__socketio-plugin.js.txt +0 -621
  185. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__storage__local-storage.js.txt +0 -218
  186. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__storage__s3-storage.js.txt +0 -124
  187. package/audit/evidence/code/critical-pass1/json-rest-schema__src__CorePlugin.js.txt +0 -269
  188. package/audit/evidence/code/critical-pass1/json-rest-schema__src__Schema.js.txt +0 -242
  189. package/audit/evidence/code/critical-pass1/json-rest-schema__src__index.js.txt +0 -77
  190. package/audit/evidence/code/critical-pass1-risk-keywords.txt +0 -238
  191. package/audit/evidence/code/critical-pass1-summary.csv +0 -67
  192. package/audit/evidence/code/critical-pass2-summary.csv +0 -67
  193. package/audit/evidence/code/early-silent-zones-no-test-token.txt +0 -57
  194. package/audit/evidence/code/error-model-signals.txt +0 -98
  195. package/audit/evidence/code/git-files-json-rest-api.txt +0 -171
  196. package/audit/evidence/code/git-files-json-rest-schema.txt +0 -27
  197. package/audit/evidence/code/hook-flow-signals.txt +0 -282
  198. package/audit/evidence/code/idproperty-signals.txt +0 -157
  199. package/audit/evidence/code/import-cycles-non-anyapi.txt +0 -3
  200. package/audit/evidence/code/import-hotspots-non-anyapi.txt +0 -96
  201. package/audit/evidence/code/imports-json-rest-api.txt +0 -354
  202. package/audit/evidence/code/imports-json-rest-schema.txt +0 -15
  203. package/audit/evidence/code/in-scope-files-json-rest-api.txt +0 -160
  204. package/audit/evidence/code/in-scope-files-json-rest-schema.txt +0 -13
  205. package/audit/evidence/code/non-anyapi-core-files.txt +0 -101
  206. package/audit/evidence/code/permission-signals.txt +0 -193
  207. package/audit/evidence/code/query-include-signals.txt +0 -687
  208. package/audit/evidence/code/transaction-signals.txt +0 -28
  209. package/audit/evidence/code/validation-signals.txt +0 -650
  210. package/audit/evidence/code/write-path-signals.txt +0 -61
  211. package/audit/evidence/docs/docs-claims-signals.txt +0 -1075
  212. package/audit/evidence/docs/schema-docs-claims-signals.txt +0 -179
  213. package/audit/evidence/external/corpus-freeze.md +0 -21
  214. package/audit/evidence/external/npm-competitor-metadata.txt +0 -70
  215. package/audit/evidence/external/npm-download-signals.txt +0 -23
  216. package/audit/evidence/external/npm-jsonapi-server.txt +0 -8
  217. package/audit/evidence/external/npm-modern-jsonapi-metadata.txt +0 -38
  218. package/audit/evidence/external/readme-fortune-signals.txt +0 -26
  219. package/audit/evidence/external/readme-fortune.md +0 -165
  220. package/audit/evidence/external/readme-json-api-serializer-signals.txt +0 -80
  221. package/audit/evidence/external/readme-json-api-serializer.md +0 -690
  222. package/audit/evidence/external/readme-jsonapi-serde-server-signals.txt +0 -0
  223. package/audit/evidence/external/readme-jsonapi-serde-server.md +0 -22
  224. package/audit/evidence/external/readme-jsonapi-serializer-signals.txt +0 -63
  225. package/audit/evidence/external/readme-jsonapi-serializer.md +0 -315
  226. package/audit/evidence/external/readme-jsonapi-server-signals.txt +0 -19
  227. package/audit/evidence/external/readme-jsonapi-server.md +0 -104
  228. package/audit/evidence/external/readme-nestjs-json-api-signals.txt +0 -4
  229. package/audit/evidence/external/readme-nestjs-json-api.md +0 -94
  230. package/audit/evidence/external/readme-yaser-json-api-nestjs-signals.txt +0 -47
  231. package/audit/evidence/external/readme-yaser-json-api-nestjs.md +0 -447
  232. package/audit/evidence/external/standard-jsonapi-snippets.txt +0 -84
  233. package/audit/evidence/external/standard-rfc-snippets.txt +0 -168
  234. package/audit/evidence/external/standards-sources.txt +0 -6
  235. package/audit/evidence/runtime/baseline-commits.txt +0 -2
  236. package/audit/evidence/runtime/db-adapter-assumptions.txt +0 -3
  237. package/audit/evidence/runtime/environment.txt +0 -4
  238. package/audit/evidence/runtime/invariant-runtime-mapping.md +0 -38
  239. package/audit/evidence/runtime/probe-patch-relationship-error.exitcode +0 -1
  240. package/audit/evidence/runtime/probe-patch-relationship-error.log +0 -76
  241. package/audit/evidence/runtime/probe-patch-relationship-error.mjs +0 -67
  242. package/audit/evidence/runtime/probe-patch-relationship-referenceerror.exitcode +0 -1
  243. package/audit/evidence/runtime/probe-patch-relationship-referenceerror.log +0 -62
  244. package/audit/evidence/runtime/probe-patch-relationship-referenceerror.mjs +0 -47
  245. package/audit/evidence/runtime/test-exitcodes.txt +0 -4
  246. package/audit/evidence/runtime/test-summary.md +0 -107
  247. package/audit/evidence/runtime/trace-anchors.txt +0 -61
  248. package/audit/evidence/tests/access-multitenancy-test-signals.txt +0 -125
  249. package/audit/evidence/tests/failing-tests-summary.txt +0 -25
  250. package/audit/evidence/tests/npm-test-json-rest-api.exitcode +0 -1
  251. package/audit/evidence/tests/npm-test-json-rest-api.log +0 -5885
  252. package/audit/evidence/tests/npm-test-json-rest-schema.exitcode +0 -1
  253. package/audit/evidence/tests/npm-test-json-rest-schema.log +0 -356
  254. package/audit/evidence/tests/query-include-test-signals.txt +0 -948
  255. package/audit/evidence/tests/relationship-test-signals.txt +0 -223
  256. package/audit/evidence/tests/skipped-tests.txt +0 -10
  257. package/audit/evidence/tests/test-suite-list-non-anyapi.txt +0 -30
  258. package/audit/evidence/tests/test-suite-list.txt +0 -32
  259. package/audit/subsystems/docs-drift.md +0 -49
  260. package/audit/subsystems/pass2-cross-review.md +0 -131
  261. package/audit/subsystems/plugins-access.md +0 -51
  262. package/audit/subsystems/plugins-bulk.md +0 -50
  263. package/audit/subsystems/plugins-cors.md +0 -48
  264. package/audit/subsystems/plugins-file.md +0 -53
  265. package/audit/subsystems/plugins-multihome.md +0 -50
  266. package/audit/subsystems/plugins-positioning.md +0 -54
  267. package/audit/subsystems/plugins-socketio.md +0 -50
  268. package/audit/subsystems/query-engine.md +0 -61
  269. package/audit/subsystems/relationships.md +0 -55
  270. package/audit/subsystems/rest-orchestration.md +0 -66
  271. package/audit/subsystems/schema-core.md +0 -64
  272. package/audit/subsystems/storage-knex.md +0 -57
  273. package/audit/subsystems/tests.md +0 -48
  274. package/audit/subsystems/transport-express.md +0 -55
  275. package/audit/traces/trace-read-get.md +0 -22
  276. package/audit/traces/trace-read-query.md +0 -25
  277. package/audit/traces/trace-relationship-endpoints.md +0 -31
  278. package/audit/traces/trace-write-delete.md +0 -17
  279. package/audit/traces/trace-write-patch.md +0 -26
  280. package/audit/traces/trace-write-post.md +0 -22
  281. package/audit/traces/trace-write-put.md +0 -23
  282. package/docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md +0 -707
  283. package/docs/GUIDE/GUIDE_X_Multihome.md +0 -666
  284. package/plugins/core/lib/querying-writing/payload-validators.js +0 -1314
  285. package/plugins/core/rest-api-access.js +0 -459
  286. package/plugins/core/rest-api-multihome-plugin.js +0 -314
  287. package/tests/multihome.test.js +0 -553
  288. package/tests/multihome.test.js.bak +0 -550
  289. package/tests/ownership-optout.test.js +0 -76
  290. package/tests/rest-api-access.test.js +0 -206
@@ -1,1075 +0,0 @@
1
- docs/index.md:7:JSON REST API is a lightweight, plugin-based framework that makes building REST APIs incredibly simple. With automatic validation, smart relationships, and native JSON:API support, you can focus on your business logic instead of boilerplate.
2
- docs/index.md:30: <p>Extend your API with powerful plugins. Authentication, validation, CORS, and more - just plug and play.</p>
3
- docs/index.md:49: <h3>🎯 JSON:API Compliant</h3>
4
- docs/index.md:50: <p>Follow industry standards with native JSON:API support. Compatible with any JSON:API client library.</p>
5
- docs/index.md:110:}).on('error', (err) => {
6
- docs/index.md:111: console.error('Failed to start server:', err);
7
- docs/COMPARISON.md:1:# JSON:API Library Comparison
8
- docs/COMPARISON.md:3:This document compares json-rest-api with other JSON:API libraries in the Node.js ecosystem, highlighting key differences and advantages.
9
- docs/COMPARISON.md:7:json-rest-api is a full-featured, plugin-based JSON:API implementation built on the hooked-api framework. Unlike other solutions, it offers a unique combination of compliance, flexibility, and developer experience.
10
- docs/COMPARISON.md:14:| Full JSON:API 1.1 compliance | ✅ | ⚠️ (serialization only) | ✅ | ✅ | ⚠️ |
11
- docs/COMPARISON.md:27:| Deep includes | ✅ | ⚠️ | ✅ | ⚠️ | ✅ |
12
- docs/COMPARISON.md:44:**json-api-serializer** is a serialization-only library that focuses on converting JavaScript objects to/from JSON:API format.
13
- docs/COMPARISON.md:48:- **You still need**: Routing, database layer, relationship handling, query parsing, error handling
14
- docs/COMPARISON.md:56: author: { ref: 'id', included: true }
15
- docs/COMPARISON.md:90:**nestjs-json-api** provides JSON:API support specifically for NestJS applications using decorators.
16
- docs/COMPARISON.md:94:- **Architecture**: Class-based with decorators vs plugin-based composition
17
- docs/COMPARISON.md:132:**@jsonapi/server** is a lightweight JSON:API framework that provides basic compliance.
18
- docs/COMPARISON.md:135:- **Features**: json-rest-api offers many more features (file uploads, WebSockets, bulk operations)
19
- docs/COMPARISON.md:142:**fortune.js** is a hypermedia API framework with optional JSON:API support.
20
- docs/COMPARISON.md:146:- **Focus**: Generic hypermedia vs dedicated JSON:API implementation
21
- docs/COMPARISON.md:157:// JSON:API mode (for HTTP clients)
22
- docs/COMPARISON.md:183: simplifiedTransport: false // HTTP endpoints use JSON:API format
23
- docs/COMPARISON.md:189:Unmatched flexibility through composition:
24
- docs/COMPARISON.md:274: queryParams: { include: ['commentable'] } // Works with any type
25
- docs/COMPARISON.md:319: include: [
26
- docs/COMPARISON.md:332:// 4. Returns properly structured JSON:API response
27
- docs/COMPARISON.md:341:- ✅ Full JSON:API compliance with minimal setup
28
- docs/COMPARISON.md:344:- ✅ Advanced features like file uploads, WebSockets, bulk operations
29
- docs/COMPARISON.md:345:- ✅ Both JSON:API and simplified object formats
30
- docs/COMPARISON.md:352:- ❌ You need a different hypermedia format (not JSON:API)
31
- docs/COMPARISON.md:425:json-rest-api stands out as the most comprehensive JSON:API solution for Node.js:
32
- docs/COMPARISON.md:433:For teams building JSON:API services in 2025, json-rest-api offers the best combination of compliance, features, and developer experience.
33
- docs/ONBOARDING.md:65:4. **error** - Global error handling hook
34
- docs/ONBOARDING.md:66: - Allows plugins to intercept and handle errors
35
- docs/ONBOARDING.md:147:- Transforms between formats (JSON:API ↔ simplified)
36
- docs/ONBOARDING.md:154:- Transforms between database format and JSON:API
37
- docs/ONBOARDING.md:270: } catch (error) {
38
- docs/ONBOARDING.md:271: // Handle errors
39
- docs/ONBOARDING.md:272: handleError(error, req, res);
40
- docs/ONBOARDING.md:306:1. **Plugin composition** - Each plugin does one thing well
41
- README.md:3:A powerful REST API plugin for [hooked-api](https://github.com/mobily-enterprises/hooked-api) that provides JSON:API-compliant endpoints with minimal configuration. This library makes it easy to create fully-featured REST APIs with support for relationships, filtering, sorting, pagination, and file uploads.
42
- README.md:9:* **JSON:API Compliant** - Full support for the JSON:API specification
43
- README.md:11:* **Advanced Querying** - Filtering, sorting, pagination, and field selection (sparse fieldsets)
44
- README.md:14:* **Validation** - Schema-based validation with detailed error messages and custom rules
45
- README.md:15:* **Simplified Mode** - A developer-friendly option to work with plain JavaScript objects instead of verbose JSON:API structure
46
- README.md:60:}).on('error', (err) => {
47
- README.md:61: console.error('Failed to start server:', err);
48
- docs/QUICKSTART.md:63:}).on('error', (err) => {
49
- docs/QUICKSTART.md:64: console.error('Failed to start server:', err);
50
- docs/QUICKSTART.md:147:// Method 3: Full JSON:API mode (standards compliant)
51
- docs/QUICKSTART.md:159:console.log('JSON:API response:', inspect(oxfordResult));
52
- docs/QUICKSTART.md:173:// Get publisher with included authors
53
- docs/QUICKSTART.md:176: include: ['authors']
54
- docs/QUICKSTART.md:198:3. **Full JSON:API mode**: Set `simplified: false` to use the complete JSON:API specification format for both requests and responses. This provides full standards compliance and access to all JSON:API features.
55
- docs/QUICKSTART.md:204:JSON:API response: {
56
- docs/QUICKSTART.md:248:# Get a specific publisher with included authors
57
- docs/QUICKSTART.md:249:curl "http://localhost:3000/api/publishers/1?include=authors"
58
- docs/QUICKSTART.md:257:# Get authors with sparse fields (only name and surname)
59
- docs/GUIDE/GUIDE_1_Initial_Setup.md:83: 4. `warn` - Only warnings and errors
60
- docs/GUIDE/GUIDE_1_Initial_Setup.md:84: 5. `error` - Only error messages
61
- docs/GUIDE/GUIDE_1_Initial_Setup.md:92: logLevel: 'warn' // Only show warnings and errors
62
- docs/GUIDE/GUIDE_1_Initial_Setup.md:98:- logLevel: `warn` - Only see warnings and errors
63
- docs/GUIDE/GUIDE_1_Initial_Setup.md:99:- logLevel: `error` - Only see errors
64
- docs/GUIDE/GUIDE_1_Initial_Setup.md:192:In the examples above, we're using the API in **simplified mode** (which is the default for programmatic usage). Simplified mode is a convenience feature that allows you to work with plain JavaScript objects instead of the full JSON:API document structure. However, it's important to understand that internally, everything is still processed as proper JSON:API documents.
65
- docs/GUIDE/GUIDE_1_Initial_Setup.md:233:// Expected Output (a full JSON:API record):
66
- docs/GUIDE/GUIDE_1_Initial_Setup.md:245:(Note that the full JSON:API record includes links to resources, which are automatically generated based on the request headers.)
67
- docs/GUIDE/GUIDE_1_Initial_Setup.md:249:- Input requires the full JSON:API document structure with `data`, `type`, and `attributes`
68
- docs/GUIDE/GUIDE_1_Initial_Setup.md:250:- Output returns the full JSON:API response with the same nested structure (and links)
69
- docs/GUIDE/GUIDE_1_Initial_Setup.md:260: simplifiedApi: false, // All API calls will use JSON:API format by default
70
- docs/GUIDE/GUIDE_1_Initial_Setup.md:272: simplifiedApi: false, // API calls to this resource use JSON:API format
71
- docs/GUIDE/GUIDE_1_Initial_Setup.md:289: simplifiedApi: false, // API calls to this resource use JSON:API format
72
- docs/GUIDE/GUIDE_1_Initial_Setup.md:328:By default, `simplifiedApi` is `true` for programmatic usage, making it easier to work with the API in your code while still maintaining full JSON:API compliance internally.
73
- docs/GUIDE/GUIDE_1_Initial_Setup.md:413:console.log('Full JSON:API response:', inspect(fullJsonApi));
74
- docs/GUIDE/GUIDE_1_Initial_Setup.md:415:// Full JSON:API response: {
75
- docs/GUIDE/GUIDE_1_Initial_Setup.md:435:console.log('Minimal JSON:API response:', inspect(minimalJsonApi));
76
- docs/GUIDE/GUIDE_1_Initial_Setup.md:437:// Minimal JSON:API response: { id: '5', type: 'countries' }
77
- docs/GUIDE/GUIDE_1_Initial_Setup.md:569:}).on('error', (err) => {
78
- docs/GUIDE/GUIDE_1_Initial_Setup.md:570: console.error('Failed to start server:', err);
79
- docs/GUIDE/GUIDE_1_Initial_Setup.md:649:- **HTTP/REST**: `simplifiedTransport` defaults to `false` (JSON:API compliance)
80
- docs/GUIDE/GUIDE_1_Initial_Setup.md:651:This means that by default, HTTP endpoints expect and return proper JSON:API format:
81
- docs/GUIDE/GUIDE_1_Initial_Setup.md:653:Most production servers will keep `simplifiedTransport: false` to maintain JSON:API compliance for client applications. You can enable simplified mode for HTTP if needed:
82
- docs/GUIDE/GUIDE_1_Initial_Setup.md:776:| `includeDepthLimit` | `vars.includeDepthLimit` | `3` | Maximum depth for nested relationship includes | ✓ |
83
- docs/GUIDE/GUIDE_1_Initial_Setup.md:777:| `enablePaginationCounts` | `vars.enablePaginationCounts` | `true` | Whether to include total count in pagination metadata | ✓ |
84
- docs/GUIDE/GUIDE_1_Initial_Setup.md:780:| `idProperty` | `vars.idProperty` | `'id'` | Name of the ID field in resources | ✓ |
85
- docs/GUIDE/GUIDE_1_Initial_Setup.md:826:- **`api.helpers.newTransaction()`** - Creates a new database transaction for atomic operations
86
- docs/GUIDE/GUIDE_1_Initial_Setup.md:830: // Use transaction in multiple operations
87
- docs/GUIDE/GUIDE_1_Initial_Setup.md:831: await api.resources.countries.post({ name: 'France', code: 'FR' }, { transaction: trx });
88
- docs/GUIDE/GUIDE_1_Initial_Setup.md:832: await api.resources.publishers.post({ name: 'French Press', country: 1 }, { transaction: trx });
89
- docs/GUIDE/GUIDE_1_Initial_Setup.md:834: } catch (error) {
90
- docs/GUIDE/GUIDE_1_Initial_Setup.md:835: await trx.rollback();
91
- docs/GUIDE/GUIDE_1_Initial_Setup.md:836: throw error;
92
- docs/GUIDE/GUIDE_1_Initial_Setup.md:863:- **`api.http.express.notFoundRouter`** - Express middleware for handling 404 errors
93
- docs/GUIDE/GUIDE_1_Initial_Setup.md:943:- Managing database transactions
94
- docs/GUIDE/GUIDE_2_1_The_Starting_Point.md:39:}).on('error', (err) => {
95
- docs/GUIDE/GUIDE_2_1_The_Starting_Point.md:40: console.error('Failed to start server:', err);
96
- docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md:77:Let's create a comprehensive dataset that includes all relationship types. We'll use this same dataset throughout the chapter to demonstrate how each operation affects the data.
97
- docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md:187: include: ['author', 'publisher', 'genres']
98
- docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md:225: include: ['author', 'publisher', 'genres']
99
- docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md:314: // publisher_id and genres NOT included!
100
- docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md:325:Again, PUT is a **complete replacement** operation. Since we didn't include `publisher_id` or `genres` in our request, the API treats this as "I want a book with no publisher and no genres." The result:
101
- docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md:404:- You must include ALL data you want to keep
102
- docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md:786:3. **Always include relationships you want to keep with PUT** - They will be cleared otherwise
103
- docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md:787:4. **Remember required fields** - PUT must include all required fields like author_id
104
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:3:Pagination and ordering are essential features for working with large datasets. The json-rest-api library provides powerful pagination that applies not just to main resources, but also to included relationships. This means you can limit and order both parent records AND their children independently.
105
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:21: include: {
106
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:22: limit: 5, // Default limit for included comments
107
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:31: include: {
108
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:675:- **Multi-field sorting is fully supported** - When sorting by multiple fields (e.g., `sort: ['category', 'name']`), the cursor includes all sort fields and correctly handles records with duplicate values in the first sort field
109
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:879:When you include relationships in your query, they use the default `limit` and `orderBy` values defined in the relationship schema. These defaults are applied automatically and cannot be overridden in the query:
110
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:888:// include: {
111
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:895:// When you include comments, these defaults are automatically applied:
112
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:898: include: ['comments'], // Just specify which relationships to include
113
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:902:// HTTP: GET /api/posts?include=comments&page[number]=1&page[size]=3
114
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:924: include: ['comments', 'tags'],
115
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:972: self: '/api/posts?include=comments&&sort=-created_at&page[number]=1&page[size]=3',
116
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:973: first: '/api/posts?include=comments&&sort=-created_at&page[number]=1&page[size]=3',
117
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:974: last: '/api/posts?include=comments&&sort=-created_at&page[number]=9&page[size]=3',
118
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:975: next: '/api/posts?include=comments&&sort=-created_at&page[number]=2&page[size]=3'
119
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:1018: self: '/api/posts?include=comments&include=tags&&sort=-created_at&page[number]=1&page[size]=2',
120
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:1019: first: '/api/posts?include=comments&include=tags&&sort=-created_at&page[number]=1&page[size]=2',
121
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:1020: last: '/api/posts?include=comments&include=tags&&sort=-created_at&page[number]=13&page[size]=2',
122
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:1021: next: '/api/posts?include=comments&include=tags&&sort=-created_at&page[number]=2&page[size]=2'
123
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:1028:The API automatically generates pagination links following JSON:API specification:
124
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:1037: simplified: false // Use JSON:API format to see links
125
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:1040:// Returns (JSON:API): {
126
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:1094: include: ['comments', 'tags']
127
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:1097:// HTTP: GET /api/posts?filter[title]=Even&sort=-view_count,published_at&page[number]=1&page[size]=3&include=comments,tags
128
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:1220: self: '/api/posts?filters[title]=Even&sort=-view_count&sort=published_at&include=comments&include=tags&&page[number]=1&page[size]=3',
129
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:1221: first: '/api/posts?filters[title]=Even&sort=-view_count&sort=published_at&include=comments&include=tags&&page[number]=1&page[size]=3',
130
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:1222: last: '/api/posts?filters[title]=Even&sort=-view_count&sort=published_at&include=comments&include=tags&&page[number]=4&page[size]=3',
131
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:1223: next: '/api/posts?filters[title]=Even&sort=-view_count&sort=published_at&include=comments&include=tags&&page[number]=2&page[size]=3'
132
- docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md:1232:- The system automatically uses SQL window functions for efficient pagination of included records when supported by the database
133
- docs/API.md:37: - [Sparse Fieldsets](#sparse-fieldsets)
134
- docs/API.md:41:7. [Error Handling](#error-handling)
135
- docs/API.md:64: include: Array, // Relationship paths to include
136
- docs/API.md:71: transaction: Object // Database transaction object
137
- docs/API.md:78:| `queryParams.include` | Array | No | Relationship paths to include (e.g., `['author', 'comments.user']`) |
138
- docs/API.md:88:| `transaction` | Object | No | Database transaction object |
139
- docs/API.md:92:**JSON:API Mode (simplified: false):**
140
- docs/API.md:110: included: [
141
- docs/API.md:162:GET /articles?include=author&fields[articles]=title,content&fields[users]=name&filter[status]=published&sort=-created-at&page[number]=1&page[size]=10
142
- docs/API.md:215: include: ['author', 'comments.user'],
143
- docs/API.md:225:// GET /articles?include=author,comments.user&fields[articles]=title,summary&fields[users]=name,avatar&fields[comments]=content,created-at
144
- docs/API.md:228:**JSON:API Mode Query:**
145
- docs/API.md:230:// Force JSON:API response format
146
- docs/API.md:234: include: ['author']
147
- docs/API.md:239:// Returns full JSON:API structure with type, id, attributes, relationships
148
- docs/API.md:275: include: Array, // Relationship paths to include
149
- docs/API.md:279: transaction: Object // Database transaction object
150
- docs/API.md:287:| `queryParams.include` | Array | No | Relationship paths to include |
151
- docs/API.md:290:| `transaction` | Object | No | Database transaction object |
152
- docs/API.md:294:**JSON:API Mode (simplified: false):**
153
- docs/API.md:310: included: [
154
- docs/API.md:339:GET /articles/1?include=author&fields[articles]=title,content
155
- docs/API.md:362: include: ['author', 'comments']
156
- docs/API.md:367:// GET /articles/1?include=author,comments
157
- docs/API.md:376: include: ['author'],
158
- docs/API.md:385:// GET /articles/1?include=author&fields[articles]=title,summary&fields[users]=name
159
- docs/API.md:388:**JSON:API Mode Get:**
160
- docs/API.md:390:// Get in JSON:API format
161
- docs/API.md:394: include: ['author', 'tags']
162
- docs/API.md:399:// Returns full JSON:API document structure
163
- docs/API.md:417: inputRecord: Object, // Required: Resource data (JSON:API or simplified)
164
- docs/API.md:419: include: Array, // For response formatting
165
- docs/API.md:423: transaction: Object, // Database transaction object
166
- docs/API.md:431:| `inputRecord.data` | Object | Yes (JSON:API) | Resource data container |
167
- docs/API.md:432:| `inputRecord.data.type` | String | Yes (JSON:API) | Resource type |
168
- docs/API.md:433:| `inputRecord.data.attributes` | Object | Yes (JSON:API) | Resource attributes |
169
- docs/API.md:435:| `queryParams` | Object | No | For includes/fields in response |
170
- docs/API.md:437:| `transaction` | Object | No | Database transaction object |
171
- docs/API.md:493:**Create with JSON:API Format:**
172
- docs/API.md:495:// Create article with JSON:API format
173
- docs/API.md:527: include: ['author', 'tags']
174
- docs/API.md:580: include: Array, // For response formatting
175
- docs/API.md:584: transaction: Object, // Database transaction object
176
- docs/API.md:593:| `inputRecord.data.id` | String | Yes (JSON:API) | Must match the resource ID |
177
- docs/API.md:594:| `inputRecord.data.type` | String | Yes (JSON:API) | Resource type |
178
- docs/API.md:595:| `inputRecord.data.attributes` | Object | Yes (JSON:API) | All resource attributes |
179
- docs/API.md:599:| `transaction` | Object | No | Database transaction object |
180
- docs/API.md:648:**Replace with JSON:API Format:**
181
- docs/API.md:650:// Replace article with JSON:API format
182
- docs/API.md:707: include: Array, // For response formatting
183
- docs/API.md:711: transaction: Object, // Database transaction object
184
- docs/API.md:720:| `inputRecord.data.id` | String | Yes (JSON:API) | Resource ID |
185
- docs/API.md:721:| `inputRecord.data.type` | String | Yes (JSON:API) | Resource type |
186
- docs/API.md:726:| `transaction` | Object | No | Database transaction object |
187
- docs/API.md:779:**Update with JSON:API Format:**
188
- docs/API.md:781:// Update with JSON:API format
189
- docs/API.md:841: transaction: Object // Database transaction object
190
- docs/API.md:848:| `transaction` | Object | No | Database transaction object |
191
- docs/API.md:878:// Delete within a transaction
192
- docs/API.md:879:const trx = await knex.transaction();
193
- docs/API.md:884: transaction: trx
194
- docs/API.md:890: transaction: trx
195
- docs/API.md:894:} catch (error) {
196
- docs/API.md:895: await trx.rollback();
197
- docs/API.md:896: throw error;
198
- docs/API.md:901:The library automatically manages transaction commits when you don't provide one:
199
- docs/API.md:902:- If you provide a transaction, you're responsible for committing/rolling back
200
- docs/API.md:903:- If you don't provide a transaction, the library creates one and auto-commits
201
- docs/API.md:925: transaction: Object // Database transaction object
202
- docs/API.md:934:| `transaction` | Object | No | Database transaction object |
203
- docs/API.md:938:JSON:API response with related resources (supports all query features like filtering, pagination, etc.)
204
- docs/API.md:1004: transaction: Object // Database transaction object
205
- docs/API.md:1010:JSON:API relationship document with resource identifiers only
206
- docs/API.md:1078: transaction: Object // Database transaction object
207
- docs/API.md:1135: transaction: Object // Database transaction object
208
- docs/API.md:1216: transaction: Object // Database transaction object
209
- docs/API.md:1305:14. afterCommit (if transaction was created)
210
- docs/API.md:1323:14. afterCommit (if transaction was created)
211
- docs/API.md:1341:14. afterCommit (if transaction was created)
212
- docs/API.md:1353:8. afterCommit (if transaction was created)
213
- docs/API.md:1425: transaction: {...}, // Database transaction
214
- docs/API.md:1460: transaction: {...},
215
- docs/API.md:1467:- Throw errors for custom validation
216
- docs/API.md:1489: transaction: {...}
217
- docs/API.md:1493:**Purpose:** Authorization checks - throw error to deny access
218
- docs/API.md:1514: include: ['author'],
219
- docs/API.md:1520: transaction: {...},
220
- docs/API.md:1548: record: { // Full JSON:API record
221
- docs/API.md:1554: isMainResource: true, // vs included resource
222
- docs/API.md:1556: requestedFields: [...], // Fields requested via sparse fieldsets
223
- docs/API.md:1598: if (context.requestedComputedFields.includes('word_count')) {
224
- docs/API.md:1603: if (context.requestedComputedFields.includes('reading_time')) {
225
- docs/API.md:1619: included: [...],
226
- docs/API.md:1647: error: {...}, // For rollback
227
- docs/API.md:1899:// Response includes:
228
- docs/API.md:1946:// Response includes:
229
- docs/API.md:1988:// With includes - specify fields for each type
230
- docs/API.md:1991: include: ['author', 'category'],
231
- docs/API.md:2014: include: ['author']
232
- docs/API.md:2021: include: ['author', 'category', 'tags']
233
- docs/API.md:2026:// GET /articles?include=author,category,tags
234
- docs/API.md:2035: include: ['author.profile', 'comments.user', 'category.parent']
235
- docs/API.md:2039:// Deep nesting (limited by includeDepthLimit)
236
- docs/API.md:2042: include: ['comments.user.profile.avatar']
237
- docs/API.md:2047:// GET /articles?include=comments.user.profile.avatar
238
- docs/API.md:2052:Some implementations support filtering included resources:
239
- docs/API.md:2055:// Custom hook to filter included resources
240
- docs/API.md:2057: if (context.result.included) {
241
- docs/API.md:2058: // Filter included comments to only show approved
242
- docs/API.md:2059: context.result.included = context.result.included.filter(resource => {
243
- docs/API.md:2079: simplifiedTransport: false, // Use JSON:API for HTTP transport (default: false)
244
- docs/API.md:2101: includeDepthLimit: 3, // Maximum relationship nesting depth
245
- docs/API.md:2107: exposeErrors: false, // Include error details in responses
246
- docs/API.md:2127: idProperty: 'id', // Custom primary key field (default: 'id')
247
- docs/API.md:2257: includeDeleted: false
248
- docs/API.md:2281: idProperty: 'user_id', // Use 'user_id' instead of 'id'
249
- docs/API.md:2347:The library uses standard JSON:API error format:
250
- docs/API.md:2353: errors: [
251
- docs/API.md:2376: errors: [{
252
- docs/API.md:2389: errors: [{
253
- docs/API.md:2401: errors: [{
254
- docs/API.md:2413: errors: [{
255
- docs/API.md:2429: const error = new Error('Title too short');
256
- docs/API.md:2430: error.status = 422;
257
- docs/API.md:2431: error.code = 'TITLE_TOO_SHORT';
258
- docs/API.md:2432: error.pointer = '/data/attributes/title';
259
- docs/API.md:2433: throw error;
260
- docs/API.md:2437:// Custom error transformation
261
- docs/API.md:2438:api.hook('errorTransform', async (error, context) => {
262
- docs/API.md:2440: status: error.status || '500',
263
- docs/API.md:2441: code: error.code || 'INTERNAL_ERROR',
264
- docs/API.md:2442: title: error.title || 'Error',
265
- docs/API.md:2443: detail: error.message,
266
- docs/API.md:2458:All methods support database transactions with automatic management:
267
- docs/API.md:2461:// Automatic transaction (recommended)
268
- docs/API.md:2467: // No transaction provided - library creates and auto-commits
269
- docs/API.md:2470:// Manual transaction management
270
- docs/API.md:2471:const trx = await knex.transaction();
271
- docs/API.md:2479: transaction: trx // Provide transaction
272
- docs/API.md:2489: transaction: trx // Same transaction
273
- docs/API.md:2494:} catch (error) {
274
- docs/API.md:2495: await trx.rollback();
275
- docs/API.md:2496: throw error;
276
- docs/API.md:2500:**Important:** When you provide a transaction, you're responsible for committing/rolling back. When you don't provide one, the library auto-commits.
277
- docs/API.md:2507:// Batch create with transaction
278
- docs/API.md:2509: const trx = await knex.transaction();
279
- docs/API.md:2516: transaction: trx,
280
- docs/API.md:2524: } catch (error) {
281
- docs/API.md:2525: await trx.rollback();
282
- docs/API.md:2526: throw error;
283
- docs/API.md:2532: const trx = await knex.transaction();
284
- docs/API.md:2538: transaction: trx,
285
- docs/API.md:2544: } catch (error) {
286
- docs/API.md:2545: await trx.rollback();
287
- docs/API.md:2546: throw error;
288
- docs/API.md:2620: include: ['commentable'], // Includes the related article/video/photo
289
- docs/API.md:2636: includeDeleted: false // Default behavior
290
- docs/API.md:2654: include_deleted: true
291
- docs/API.md:2683: if (context.requestedComputedFields.includes('can_edit')) {
292
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:64:// Get the French publisher and include its authors (simplified mode output)
293
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:68: include: ['authors'] // Use the relationship name 'authors' defined in the publishers schema
294
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:107:## Including `hasMany` Records (`include`)
295
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:109:To retrieve related `hasMany` resources, you'll use the `include` query parameter from the "one" side of the one-to-many relationship (e.g., fetching a publisher and including its authors).
296
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:113:Using the exact same data as before, you can change the query to `include` countries:
297
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:116:// Get the French publisher and include its authors (simplified mode output)
298
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:120: include: ['authors'] // Use the relationship name 'authors' defined in the publishers schema
299
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:125:// Get the French publisher and include its authors (simplified mode output)
300
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:129: include: ['authors'] // Use the relationship name 'authors' defined in the publishers schema
301
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:155: included: [
302
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:179:// Query all publishers and include their authors (simplified mode output)
303
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:182: include: ['authors']
304
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:185:// HTTP: GET /api/publishers?include=authors
305
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:205:// Query all publishers and include their authors (non-simplified, full JSON:API output)
306
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:208: include: ['authors']
307
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:212:// HTTP: GET /api/publishers?include=authors
308
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:213:// Returns (JSON:API): {
309
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:235:// included: [
310
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:320: included: [
311
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:343: links: { self: '/api/publishers?include=authors' }
312
- docs/GUIDE/GUIDE_2_4_HasMany_Records.md:349:In non-simplified (full JSON:API) mode, `hasMany` relationships in the `data` section of the parent resource only contain an empty `data` array or `links` to the related endpoint (e.g., `authors: { links: { related: '/api/publishers/1/authors' } }`). The actual related `author` resources are placed in the top-level `included` array. This is standard JSON:API behavior to avoid duplicating large amounts of data. The `included` array ensures that each included resource appears only once, even if referenced by multiple parent resources.
313
- docs/GUIDE/GUIDE_2_9_Relationships_Urls.md:3:The REST API Relationships Plugin adds JSON:API compliant relationship endpoints to your API, allowing clients to view and manage relationships between resources as first-class citizens. This guide shows you how to use these powerful features with our book catalog system.
314
- docs/GUIDE/GUIDE_2_9_Relationships_Urls.md:17:- [Error Handling](#error-handling)
315
- docs/GUIDE/GUIDE_2_9_Relationships_Urls.md:27:4. **Atomic Operations**: Manage relationships in isolation with proper transaction support
316
- docs/GUIDE/GUIDE_2_9_Relationships_Urls.md:102: simplified: false // Use full JSON:API format
317
- docs/GUIDE/GUIDE_2_9_Relationships_Urls.md:152: fields: { authors: 'name' } // Optional: sparse fieldsets
318
- docs/GUIDE/GUIDE_2_9_Relationships_Urls.md:393:Common errors you might encounter:
319
- docs/GUIDE/GUIDE_2_9_Relationships_Urls.md:402:} catch (error) {
320
- docs/GUIDE/GUIDE_2_9_Relationships_Urls.md:416:} catch (error) {
321
- docs/GUIDE/GUIDE_2_9_Relationships_Urls.md:428:} catch (error) {
322
- docs/GUIDE/GUIDE_2_9_Relationships_Urls.md:497:The plugin automatically handles transactions for data integrity:
323
- docs/GUIDE/GUIDE_2_9_Relationships_Urls.md:515:The REST API Relationships Plugin transforms relationships from second-class citizens to fully manageable resources. It provides efficient, standards-compliant endpoints that make working with related data intuitive and performant. By following JSON:API specifications, it ensures your API remains consistent and predictable for clients.
324
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:5:Let's expand our schema definitions to include `publishers` and link them to `countries`. These schemas will be defined **once** here and reused throughout this section.
325
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:93:## Including `belongsTo` Records (`include`)
326
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:95:To retrieve related `belongsTo` resources, use the `include` query parameter.
327
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:97:When fetching data programmatically, `simplified` mode is `true` by default. This means that instead of a separate `included` array (as in full JSON:API), related `belongsTo` resources are **denormalized and embedded directly** within the main resource's object structure, providing a very convenient and flat data structure for immediate use.
328
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:119:// Get a publisher and include its country (simplified mode output)
329
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:123: include: ['country'] // Use the 'as' alias defined in the schema
330
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:128:// Query all publishers and include their countries (simplified mode output)
331
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:131: include: ['country']
332
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:134:// HTTP: GET /api/publishers?include=country
333
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:145:// Query all publishers and include their countries (JSON:API format)
334
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:148: include: ['country']
335
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:152:// HTTP: GET /api/publishers?include=country
336
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:153:// Returns (JSON:API): {
337
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:164:// included: [
338
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:174:Here is the expected output. Notice how the last call shows the non-simplified version of the response, which is muc more verbose. However, it has one _major_ advantage: it only includes the information about France _once_. It might seem like a small gain here, but when you have complex queries where the `belongsTo` table has a lot of data, the saving is much more evident.
339
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:288: included: [
340
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:311: links: { self: '/api/publishers?include=country' }
341
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:319:You can apply **sparse fieldsets** not only to the primary resource but also to the included `belongsTo` resources. This is powerful for fine-tuning your API responses and reducing payload sizes.
342
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:336:// Get a publisher, include its country, but only retrieve publisher name and country code
343
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:337:const sparsePublisher = await api.resources.publishers.get({
344
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:340: include: ['country'],
345
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:348:console.log('Sparse Publisher and Country:', inspect(sparsePublisher));
346
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:351:// Query all publishers, include their countries, but only retrieve publisher name and country code
347
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:352:const sparsePublishersQuery = await api.resources.publishers.query({
348
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:354: include: ['country'],
349
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:362:// HTTP: GET /api/publishers?include=country&fields[publishers]=name&fields[countries]=code,name
350
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:369:console.log('Sparse Publishers Query (all results):', inspect(sparsePublishersQuery));
351
- docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md:375:When you specify `fields: { countries: ['code'] }`, this instruction applies to *all* `country` resources present in the API response, whether `country` is the primary resource you are querying directly, or if it's included as a related resource. This ensures consistent data representation across the entire response.
352
- docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md:144: const { data, error } = await supabase.auth.signUp({
353
- docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md:148: if (error) throw error
354
- docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md:154: const { data, error } = await supabase.auth.signInWithPassword({
355
- docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md:158: if (error) throw error
356
- docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md:249: const { data, error } = await supabase.auth.signInWithOAuth({
357
- docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md:255: if (error) throw error
358
- docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md:308: ownershipField: 'user_id' // Field for ownership checks (default: 'user_id')
359
- docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md:469:// Also enable auto-ownership
360
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:198:## Including Many-to-Many Records (`include`)
361
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:206:console.log('Book without includes (simplified):', inspect(book_simplified));
362
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:207:console.log('Book without includes (non-simplified):', inspect(book_non_simplified));
363
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:213:Book without includes (simplified):
364
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:222:Book without includes (non-simplified):
365
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:246:## Including Many to many Records (`include`)
366
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:248:To retrieve the full related resources through a many-to-many relationship, use the `include` query parameter:
367
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:251:// Get a book with its authors included
368
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:254: queryParams: { include: ['authors'] }
369
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:259: queryParams: { include: ['authors'] },
370
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:265: queryParams: { include: ['authors'] }
371
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:269: queryParams: { include: ['authors'] },
372
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:325: included: [
373
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:409: links: { self: '/api/books?include=authors' }
374
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:474: included: [
375
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:509: links: { self: '/api/books?include=authors' }
376
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:514:The include system automatically handles the JOIN through the pivot table. In simplified mode, the related resources are embedded directly. In JSON:API mode, they appear in the `included` array.
377
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:520:Using the schema definitions from above, which include cross-table search fields:
378
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:543:// 3. Combine searches: Find books by Neil that include full author data
379
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:547: include: ['authors']
380
- docs/GUIDE/GUIDE_2_6_Many_To_Many.md:690: links: { self: '/api/books?filters[authorName]=Neil&include=authors' }
381
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:90:- Schema includes `reviewable_type` and `reviewable_id` fields
382
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:101:`json-rest-api` provides flexibility in how you provide data for polymorphic relationships when creating new records, depending on whether you're using the simplified API mode (default for programmatic calls) or the strict JSON:API format.
383
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:125:// Add reviews using non-simplified (JSON:API standard) input with relationships object
384
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:174:// Returns (JSON:API): {
385
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:204:* **Non-Simplified Mode (JSON:API Standard):** This adheres to the JSON:API specification, where relationships are explicitly defined in the `relationships` object with a resource identifier object (`{ type: 'resourceType', id: 'resourceId' }`). This is typically used when interacting with the API via HTTP or when strict JSON:API compliance is required.
386
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:314:When you fetch the french author after adding the reviews, you only get the review _ids_ and not the full review data. This is expected. To fetch the actual reviews, you will need to `include` them.
387
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:316:## Including Polymorphic Records (`include`)
388
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:318:To retrieve related polymorphic resources (e.g., getting a publisher and including all its reviews, or getting an author and including all their reviews), you'll use the **`include` query parameter** from the "one" side of the polymorphic relationship.
389
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:325:const frenchAuthor1_with_reviews_and_includes_simplified = await api.resources.authors.get({ id: frenchAuthor1_ns.id, queryParams: { include: ['reviews'] } });
390
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:326:const frenchAuthor1_with_reviews_and_includes_non_simplified = await api.resources.authors.get({ id: frenchAuthor1_ns.id, queryParams: { include: ['reviews'] }, simplified: false });
391
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:328:const french_authors_with_includes_simplified = await api.resources.authors.query({queryParams: { include: ['reviews'] } });
392
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:329:// HTTP: GET /api/authors?include=reviews
393
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:337:const french_authors_with_includes_non_simplified = await api.resources.authors.query({queryParams: { include: ['reviews'] }, simplified: false });
394
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:338:// HTTP: GET /api/authors?include=reviews
395
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:339:// Returns (JSON:API): {
396
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:348:// included: [
397
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:356:console.log(inspect(frenchAuthor1_with_reviews_and_includes_simplified));
398
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:358:console.log(inspect(frenchAuthor1_with_reviews_and_includes_non_simplified))
399
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:361:console.log(inspect(french_authors_with_includes_simplified));
400
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:363:console.log(inspect(french_authors_with_includes_non_simplified))
401
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:409: included: [
402
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:500: included: [
403
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:516: links: { self: '/api/authors?include=reviews' }
404
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:522:When you explicitly request **non-simplified output**, the polymorphic `hasMany` relationships will appear in the **`included` array** at the top level of the JSON:API document. The parent resource's `relationships` object will contain links to the related endpoint but not the full related data itself.
405
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:530:This happens when you want to search reviews, and you want to also search in the reviewed item's information. For example searching for 'Apress' would return all reviews where the publisher name includes `Apress`. this is a very common scenario. This is achieved via cross-table filtering.
406
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:699:// Returns (JSON:API): {
407
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:718:// Returns (JSON:API): {
408
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:730:// Returns (JSON:API): {
409
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:860:The output demonstrates how polymorphic search works across both simplified and JSON:API formats:
410
- docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md:869:In the simplified format, notice how the polymorphic fields are included:
411
- docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md:41:You can do the same thing talking to the server directly (although you will be dealing with JSON:API results).
412
- docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md:120:As you can see, using PUT on Italy was a problem: since put didn't include the `code` field, and since PUT assumes a FULL record, the `code` field was reset to null. On the other hand, since the method PATCH assumes a partial update, the update for Germany did not _not_ overwrite the `code` field. This is a very important distinction, and it's the reason why most clients avoid PUT calls.
413
- docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md:373:// Result: The effective searchSchema includes:
414
- docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md:617:The JSON:API specification includes a powerful feature called "sparse fieldsets" that allows you to request only specific fields from a resource. This is essential for optimizing API performance by reducing payload sizes and network traffic.
415
- docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md:621:By default, API responses include all fields defined in the schema. With sparse fieldsets, you can specify exactly which fields you want returned. The `id` field is always included automatically as it's required by JSON:API.
416
- docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md:770:const sparseSearch = await api.resources.countries.query({
417
- docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md:781:console.log('Sparse search result:', inspect(sparseSearch));
418
- docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md:782:// Note: sparseSearch contains { data, meta, links }
419
- docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md:787:1. **The `id` field is always included** - This is required by the JSON:API specification
420
- docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md:790:4. **Works with relationships** - When we cover relationships, you'll see how to apply sparse fieldsets to related resources too
421
- docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md:794:When using the HTTP API, sparse fieldsets are specified as comma-separated values:
422
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:7:Inside hooks, JSON:API format is king _regardless of simplified mode_.
423
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:9: - **Input**: How parameters are passed to the API (simplified data is converted to JSON:API before entering the lifecycle)
424
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:10: - **Output**: How the record is returned to the client (JSON:API data is converted to simplified format before returning)
425
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:11:Hook context _always_ contains: Full JSON:API formatted records with `data`, `type`, `attributes`, and `relationships`. So, as far as the hooks are concerned, `context.inputRecord` is _always_ a full JSON:API object.
426
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:13:This means when writing hooks, you always work with the standard JSON:API structure:
427
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:15:// In a hook, the record is ALWAYS JSON:API format:
428
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:18: // Even in simplified mode, inputRecord has JSON:API structure
429
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:27: // This is called for ALL records (main and included/child records)
430
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:154: // This will enrich all attributes (main and included) when fetched
431
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:158: // This wordCount is calculated on-the-fly and included in the response
432
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:208: return vars.allowedStatuses.includes(status);
433
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:297:5. **Handle Errors Gracefully** - Thrown errors in hooks will stop the operation and return to the client
434
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:316: - `idProperty` - Primary key field name
435
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:319:- `transaction` (object/null) - Database transaction if provided
436
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:320:- `db` (object) - Database connection (knex instance or transaction)
437
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:324:- `inputRecord` (object) - The JSON:API formatted input containing:
438
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:334: - `include` - Relationships to include
439
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:338:- `record` (object) - The fetched/created record in JSON:API format
440
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:350:6. **Transaction hooks** - Commit/rollback for write operations
441
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:369: - `idProperty` - Primary key field name
442
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:372: - `include` - Relationships to include
443
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:376:- `transaction` (object/null) - Database transaction if provided
444
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:377:- `db` (object) - Database connection (knex instance or transaction)
445
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:387:- `queryParams.include` - Modify includes
446
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:423:- `record` (object) - The JSON:API formatted response
447
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:425: - `included` - Array of included resources
448
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:485: - `include` - Relationships to include
449
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:486:- `transaction` (object/null) - Database transaction if provided
450
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:495:- `queryParams.include` - Modify includes
451
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:511:- `record` (object) - The fetched JSON:API record
452
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:514:- Can throw errors to deny access
453
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:546:- `record` (object) - The JSON:API formatted response
454
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:590:- `inputRecord` (object) - The JSON:API formatted input data
455
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:595:- `queryParams` (object) - Contains `fields`, `include` for response
456
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:596:- `transaction` (object) - Database transaction (created if not provided)
457
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:597:- `shouldCommit` (boolean) - Whether to commit the transaction
458
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:613:**Note**: `context.minimalRecord` is always a JSON:API resource object; for POST requests it is a snapshot of the incoming payload (`{ type, id?, attributes, relationships }`).
459
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:690:- `record` (object) - The JSON:API formatted response
460
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:700:**When**: After the transaction is committed (only if `shouldCommit` is true)
461
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:726:**When**: If an error occurs and transaction is rolled back
462
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:729:**Context**: All accumulated context plus error information
463
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:757:- `inputRecord` (object) - The JSON:API formatted input data
464
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:760:- `queryParams` (object) - Contains `fields`, `include` for response
465
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:761:- `transaction` (object) - Database transaction
466
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:762:- `shouldCommit` (boolean) - Whether to commit the transaction
467
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:832:- `record` (object) - The JSON:API formatted response
468
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:850:**When**: After the transaction is committed
469
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:856:**When**: If an error occurs and transaction is rolled back
470
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:857:**Context**: All accumulated context plus error information
471
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:885:- `inputRecord` (object) - The JSON:API formatted input data (partial)
472
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:888:- `queryParams` (object) - Contains `fields`, `include` for response
473
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:889:- `transaction` (object) - Database transaction
474
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:890:- `shouldCommit` (boolean) - Whether to commit the transaction
475
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:957:- `record` (object) - The JSON:API formatted response
476
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:975:**When**: After the transaction is committed
477
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:981:**When**: If an error occurs and transaction is rolled back
478
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:982:**Context**: All accumulated context plus error information
479
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:1010:- `transaction` (object) - Database transaction
480
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:1011:- `shouldCommit` (boolean) - Whether to commit the transaction
481
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:1018:- Can throw errors to prevent deletion
482
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:1093:**When**: After the transaction is committed
483
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:1101:**When**: If an error occurs and transaction is rolled back
484
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:1102:**Context**: All accumulated context plus error information
485
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:1121:The `enrichAttributes` hook is the correct way to add or modify attributes on records. This hook is called for ALL records - both main records and included/related records.
486
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:1141:- This hook is called for EVERY record (main and included)
487
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:1308:Throwing an error in any hook will:
488
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:1310:- Trigger rollback for write operations
489
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:1311:- Return the error to the client
490
- docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md:1328:- Use database transactions appropriately
491
- docs/GUIDE/index.md:25: Implement drag-and-drop functionality with automatic position management.
492
- docs/GUIDE/index.md:37: Handle multiple records efficiently with bulk create, update, and delete operations.
493
- docs/GUIDE/index.md:39:- **[CORS Configuration](GUIDE_X_Cors.md)**
494
- docs/GUIDE/GUIDE_2_Data_And_Relations.md:3:The true power of json-rest-api lies in its sophisticated handling of relationships between resources. Unlike traditional ORMs that focus on object mapping, this library provides a fully JSON:API compliant REST interface that elegantly manages complex data relationships. This guide will walk you through defining datasets and explore all the possible relationship types available in the system.
495
- docs/GUIDE/GUIDE_2_Data_And_Relations.md:47:- Limits and ordering for included relationships
496
- docs/GUIDE/GUIDE_2_Data_And_Relations.md:48:- Combining filters, sorting, pagination, and includes
497
- docs/GUIDE/GUIDE_X_Cors.md:1:# CORS Plugin Guide
498
- docs/GUIDE/GUIDE_X_Cors.md:3:Cross-Origin Resource Sharing (CORS) is essential for APIs accessed by web browsers. This guide shows you how to use the CORS plugin to enable cross-origin requests in your JSON REST API.
499
- docs/GUIDE/GUIDE_X_Cors.md:13:## Intro: What is CORS
500
- docs/GUIDE/GUIDE_X_Cors.md:15:CORS stands for **Cross-Origin Resource Sharing**. It's a security feature implemented by web browsers to protect users from malicious websites.
501
- docs/GUIDE/GUIDE_X_Cors.md:17:**The server ALWAYS responds to requests regardless of origin.** CORS is enforced by the BROWSER, not the server. This is a crucial distinction:
502
- docs/GUIDE/GUIDE_X_Cors.md:20:- 🛡️ **Browsers**: Block the response from reaching JavaScript if CORS headers don't allow it
503
- docs/GUIDE/GUIDE_X_Cors.md:21:- 🔓 **Non-browser clients** (cURL, Postman, mobile apps): Not affected by CORS at all
504
- docs/GUIDE/GUIDE_X_Cors.md:28:- Without CORS, the browser will block requests from the web app to the API
505
- docs/GUIDE/GUIDE_X_Cors.md:30:CORS allows servers to specify which origins (domains) are permitted to access their resources. Here's the flow:
506
- docs/GUIDE/GUIDE_X_Cors.md:34:3. **Server Response**: The server ALWAYS responds with data + CORS headers
507
- docs/GUIDE/GUIDE_X_Cors.md:50:Without CORS headers from the API:
508
- docs/GUIDE/GUIDE_X_Cors.md:54:❌ Console error: "CORS policy: No 'Access-Control-Allow-Origin' header"
509
- docs/GUIDE/GUIDE_X_Cors.md:59:With CORS headers from the API:
510
- docs/GUIDE/GUIDE_X_Cors.md:62:✅ API includes: Access-Control-Allow-Origin: https://coolweather.app
511
- docs/GUIDE/GUIDE_X_Cors.md:67:**CORS does NOT protect your API from:**
512
- docs/GUIDE/GUIDE_X_Cors.md:74:**CORS ONLY protects:**
513
- docs/GUIDE/GUIDE_X_Cors.md:78:**Therefore:** CORS is about protecting users, not protecting your API. You still need:
514
- docs/GUIDE/GUIDE_X_Cors.md:86:The CORS plugin automatically handles preflight requests and adds appropriate headers to all responses.
515
- docs/GUIDE/GUIDE_X_Cors.md:88:The basic CORS setup creates a permissive configuration suitable for development:
516
- docs/GUIDE/GUIDE_X_Cors.md:102:The combination of `origin: '*'` (wildcard) and `credentials: true` is actually invalid according to CORS specification. When credentials are enabled, browsers require a specific origin. The CORS plugin handles this by dynamically setting the origin to match the request.
517
- docs/GUIDE/GUIDE_X_Cors.md:106:Here's a complete API setup with CORS enabled with the most permissive setup:
518
- docs/GUIDE/GUIDE_X_Cors.md:148:app.listen(3000).on('error', (err) => {
519
- docs/GUIDE/GUIDE_X_Cors.md:149: console.error('Failed to start server:', err);
520
- docs/GUIDE/GUIDE_X_Cors.md:233:The CORS plugin accepts various configuration options.
521
- docs/GUIDE/GUIDE_X_Cors.md:249:Each example below shows different ways to configure CORS for specific scenarios. We'll explain what each does, why you'd use it, and provide real-world context.
522
- docs/GUIDE/GUIDE_X_Cors.md:303:- For each allowed origin, the response includes that specific origin
523
- docs/GUIDE/GUIDE_X_Cors.md:415: if (origin.includes('localhost') && process.env.NODE_ENV === 'development') {
524
- docs/GUIDE/GUIDE_X_Cors.md:483:These are headers the browser is allowed to include in requests:
525
- docs/GUIDE/GUIDE_X_Cors.md:523:These examples show typical CORS configurations for different scenarios you'll encounter in real projects.
526
- docs/GUIDE/GUIDE_X_Cors.md:547:- ✅ Any developer can work with the API without CORS issues
527
- docs/GUIDE/GUIDE_X_Cors.md:742:These patterns show sophisticated CORS configurations for complex real-world scenarios.
528
- docs/GUIDE/GUIDE_X_Cors.md:747:2. **Per-Route CORS**: Some endpoints need different CORS rules
529
- docs/GUIDE/GUIDE_X_Cors.md:748:3. **Authentication Integration**: CORS and auth systems must work together
530
- docs/GUIDE/GUIDE_X_Cors.md:787: return allowed.includes(origin) || partners.includes(origin);
531
- docs/GUIDE/GUIDE_X_Cors.md:803:### Per-Route CORS (Using Hooks)
532
- docs/GUIDE/GUIDE_X_Cors.md:805:**Important Understanding:** The CORS plugin is essentially a sophisticated header management system. It:
533
- docs/GUIDE/GUIDE_X_Cors.md:812:**Why You'd Need Per-Route CORS:**
534
- docs/GUIDE/GUIDE_X_Cors.md:823: // Add extra CORS headers for admin routes
535
- docs/GUIDE/GUIDE_X_Cors.md:832:1. Hooks into the response pipeline AFTER the CORS plugin
536
- docs/GUIDE/GUIDE_X_Cors.md:834:3. Overrides the CORS headers for stricter control
537
- docs/GUIDE/GUIDE_X_Cors.md:840:// Different CORS policies for different route types
538
- docs/GUIDE/GUIDE_X_Cors.md:842: order: -900 // Run after CORS plugin (which is -1000)
539
- docs/GUIDE/GUIDE_X_Cors.md:856: if (adminOrigins.includes(request.headers.origin)) {
540
- docs/GUIDE/GUIDE_X_Cors.md:860: // Remove CORS headers entirely - block the request
541
- docs/GUIDE/GUIDE_X_Cors.md:871: if (partner && partner.allowedOrigins.includes(request.headers.origin)) {
542
- docs/GUIDE/GUIDE_X_Cors.md:889: // Special CORS for file uploads
543
- docs/GUIDE/GUIDE_X_Cors.md:903:However, this must happen before the CORS plugin is registered, since the order in which URLs are added will matter.
544
- docs/GUIDE/GUIDE_X_Cors.md:905:### CORS with Authentication
545
- docs/GUIDE/GUIDE_X_Cors.md:907:**The Critical Relationship Between CORS and Authentication**
546
- docs/GUIDE/GUIDE_X_Cors.md:909:CORS and authentication are deeply intertwined. When your API uses authentication, CORS configuration becomes security-critical.
547
- docs/GUIDE/GUIDE_X_Cors.md:913:1. **Credentials in CORS** means:
548
- docs/GUIDE/GUIDE_X_Cors.md:924: - Wrong CORS + auth = security vulnerability
549
- docs/GUIDE/GUIDE_X_Cors.md:926: - User's cookies would be automatically included
550
- docs/GUIDE/GUIDE_X_Cors.md:928:**How Authentication Flows Work with CORS:**
551
- docs/GUIDE/GUIDE_X_Cors.md:934: 3. Browser includes Origin: https://app.example.com
552
- docs/GUIDE/GUIDE_X_Cors.md:945: 2. Browser automatically includes auth cookie
553
- docs/GUIDE/GUIDE_X_Cors.md:946: 3. Browser includes Origin: https://app.example.com
554
- docs/GUIDE/GUIDE_X_Cors.md:948: 5. API responds with user data and CORS headers
555
- docs/GUIDE/GUIDE_X_Cors.md:963: if (knownOrigins.includes(origin)) {
556
- docs/GUIDE/GUIDE_X_Cors.md:1000: return allowedOrigins.includes(origin);
557
- docs/GUIDE/GUIDE_X_Cors.md:1031:**Note:** These examples show CORS configuration patterns for different authentication methods. JWT generation itself is not part of this library - you'll use your own authentication service (Supabase, Auth0, Firebase Auth, etc.) or implement your own JWT generation.
558
- docs/GUIDE/GUIDE_X_Cors.md:1048:2. **Bearer Token Pattern (No CORS Credentials):**
559
- docs/GUIDE/GUIDE_X_Cors.md:1065: // Bearer token auth - no CORS credentials needed
560
- docs/GUIDE/GUIDE_X_Cors.md:1068: // Cookie auth - needs CORS credentials
561
- docs/GUIDE/GUIDE_X_Cors.md:1074:### CORS with Base URL
562
- docs/GUIDE/GUIDE_X_Cors.md:1076:The CORS plugin works seamlessly with Express base paths.
563
- docs/GUIDE/GUIDE_X_Cors.md:1095:**How CORS Works with Base URLs:**
564
- docs/GUIDE/GUIDE_X_Cors.md:1097:The CORS plugin automatically handles the base path. When you set a base path, CORS headers are applied to ALL routes under that path.
565
- docs/GUIDE/GUIDE_X_Cors.md:1106:// CORS plugin handles the base path automatically
566
- docs/GUIDE/GUIDE_X_Cors.md:1113:// Client requests to /v1/api/articles will have proper CORS headers
567
- docs/GUIDE/GUIDE_X_Cors.md:1123:2. CORS plugin registers OPTIONS handler for `/v1/*`
568
- docs/GUIDE/GUIDE_X_Cors.md:1125:3. All requests under `/v1` get CORS headers
569
- docs/GUIDE/GUIDE_X_Cors.md:1146:// API v2 with different CORS
570
- docs/GUIDE/GUIDE_X_Cors.md:1165:// GET /v1/api/users - CORS allows legacy.example.com
571
- docs/GUIDE/GUIDE_X_Cors.md:1166:// GET /v2/api/users - CORS blocks legacy.example.com
572
- docs/GUIDE/GUIDE_X_Cors.md:1171:1. CORS applies to the entire base path, not individual routes
573
- docs/GUIDE/GUIDE_X_Cors.md:1172:2. You cannot have different CORS settings for routes under the same base path
574
- docs/GUIDE/GUIDE_X_Cors.md:1173:3. The base path is transparent to CORS origin checks
575
- docs/GUIDE/GUIDE_X_Cors.md:1182:| "CORS header 'Access-Control-Allow-Origin' missing" | Origin not allowed | Check origin configuration, ensure it matches exactly |
576
- docs/GUIDE/GUIDE_X_Cors.md:1184:| "Multiple CORS headers" | Multiple CORS middleware | Ensure CORS plugin is installed only once |
577
- docs/GUIDE/GUIDE_X_Cors.md:1185:| Preflight fails with 404 | OPTIONS route not registered | Check that CORS plugin is installed after transport plugin |
578
- docs/GUIDE/GUIDE_X_Cors.md:1188:### Debug CORS Issues
579
- docs/GUIDE/GUIDE_X_Cors.md:1198:// The CORS plugin will log:
580
- docs/GUIDE/GUIDE_X_Cors.md:1207:2024-01-15T10:23:45.123Z [DEBUG] [my-api:plugin:rest-api-cors] CORS OPTIONS request { origin: 'https://app.example.com' }
581
- docs/GUIDE/GUIDE_X_Cors.md:1208:2024-01-15T10:23:45.124Z [DEBUG] [my-api:plugin:rest-api-cors] CORS processing response {
582
- docs/GUIDE/GUIDE_X_Cors.md:1213:2024-01-15T10:23:45.125Z [WARN] [my-api:plugin:rest-api-cors] CORS origin not allowed {
583
- docs/GUIDE/GUIDE_X_Cors.md:1226: - Shows CORS headers being added to regular requests
584
- docs/GUIDE/GUIDE_X_Cors.md:1240: console.log('CORS Debug:', {
585
- docs/GUIDE/GUIDE_X_Cors.md:1252:### Testing CORS
586
- docs/GUIDE/GUIDE_X_Cors.md:1254:**Understanding CORS Testing**
587
- docs/GUIDE/GUIDE_X_Cors.md:1256:CORS is enforced by browsers, not servers. This creates interesting testing scenarios:
588
- docs/GUIDE/GUIDE_X_Cors.md:1257:- **cURL/Postman**: Requests always work (no CORS enforcement)
589
- docs/GUIDE/GUIDE_X_Cors.md:1258:- **Browser**: Requests blocked if CORS headers are wrong
590
- docs/GUIDE/GUIDE_X_Cors.md:1259:- **Server**: Always sends CORS headers, doesn't block requests
591
- docs/GUIDE/GUIDE_X_Cors.md:1271:- Default CORS configuration (allows all origins)
592
- docs/GUIDE/GUIDE_X_Cors.md:1312:1. **cURL Always Succeeds** - Even with wrong CORS headers:
593
- docs/GUIDE/GUIDE_X_Cors.md:1319:2. **Browser Testing is Required** for real CORS validation:
594
- docs/GUIDE/GUIDE_X_Cors.md:1321: // In browser console (will fail if CORS is wrong)
595
- docs/GUIDE/GUIDE_X_Cors.md:1325: .catch(e => console.error('CORS Error:', e));
596
- docs/GUIDE/GUIDE_X_Cors.md:1346:2. Check Response Headers for CORS headers
597
- docs/GUIDE/GUIDE_X_Cors.md:1347:3. For failed requests, check Console for CORS errors
598
- docs/GUIDE/GUIDE_X_Cors.md:1349:### Common CORS Headers Explained
599
- docs/GUIDE/GUIDE_X_Cors.md:1351:**Understanding Each CORS Header in Detail**
600
- docs/GUIDE/GUIDE_X_Cors.md:1353:#### Core CORS Headers
601
- docs/GUIDE/GUIDE_X_Cors.md:1370:if (allowedOrigins.includes(request.headers.origin)) {
602
- docs/GUIDE/GUIDE_X_Cors.md:1374:| `Access-Control-Allow-Credentials` | **Security Critical** - Allows browser to include cookies and auth headers | `true` |
603
- docs/GUIDE/GUIDE_X_Cors.md:1401:- Must include the requested method
604
- docs/GUIDE/GUIDE_X_Cors.md:1414:| `Access-Control-Allow-Headers` | **Preflight Only** - Lists headers the client can include | `Content-Type, Authorization, X-Requested-With` |
605
- docs/GUIDE/GUIDE_X_Cors.md:1418:- Must include all headers the client will send
606
- docs/GUIDE/GUIDE_X_Cors.md:1496:- Prevents wrong CORS headers being cached
607
- docs/GUIDE/GUIDE_X_Cors.md:1497:- Should always include `Origin` when CORS headers vary
608
- docs/GUIDE/GUIDE_X_Cors.md:1511:#### Additional CORS Headers
609
- docs/GUIDE/GUIDE_X_Cors.md:1517:| `Origin` | **Request** - Identifies requesting origin | All CORS requests |
610
- docs/GUIDE/GUIDE_X_Cors.md:1537:CORS is a security feature, but misconfiguration can create vulnerabilities. Here's what you need to know:
611
- docs/GUIDE/GUIDE_X_Cors.md:1579: credentials: 'include' // Would send victim's cookies
612
- docs/GUIDE/GUIDE_X_Cors.md:1616: return origin.includes('mycompany'); // subdomain.mycompany.evil.com would pass!
613
- docs/GUIDE/GUIDE_X_Cors.md:1640: console.warn('CORS: Rejected origin:', origin);
614
- docs/GUIDE/GUIDE_X_Cors.md:1767:**The Rule:** CORS is not a replacement for HTTPS. Always use HTTPS in production.
615
- docs/GUIDE/GUIDE_X_Cors.md:1770:CORS only controls which websites can access your API from a browser. It doesn't:
616
- docs/GUIDE/GUIDE_X_Cors.md:1776:**What CORS Does vs What HTTPS Does:**
617
- docs/GUIDE/GUIDE_X_Cors.md:1778:| Security Aspect | CORS | HTTPS |
618
- docs/GUIDE/GUIDE_X_Cors.md:1798: return allowedOrigins.includes(origin);
619
- docs/GUIDE/GUIDE_X_Cors.md:1814:**Common HTTPS + CORS Issues:**
620
- docs/GUIDE/GUIDE_X_Cors.md:1838:**The Rule:** Periodically review and audit your CORS configuration to remove unused origins and tighten security.
621
- docs/GUIDE/GUIDE_X_Cors.md:1864: console.log('=== CORS Origin Audit ===');
622
- docs/GUIDE/GUIDE_X_Cors.md:1904: console.error(`SECURITY RISK: ${origin} may be compromised`);
623
- docs/GUIDE/GUIDE_X_Cors.md:1907: } catch (error) {
624
- docs/GUIDE/GUIDE_X_Cors.md:1908: console.error(`Cannot verify ${origin}: ${error.message}`);
625
- docs/GUIDE/GUIDE_X_Cors.md:1937:// This ensures all server instances have consistent CORS settings
626
- docs/GUIDE/GUIDE_X_Cors.md:1948: origin.includes('ngrok.io') ||
627
- docs/GUIDE/GUIDE_X_Cors.md:1949: origin.includes('localhost.run') ||
628
- docs/GUIDE/GUIDE_X_Cors.md:1950: origin.includes('.local') ||
629
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:3:The **Bulk Operations Plugin** enables efficient processing of multiple records in a single request, supporting atomic transactions, batch processing, and error handling. This guide demonstrates how to create, update, and delete multiple records efficiently.
630
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:14:- **bulkPost**: Create multiple records
631
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:15:- **bulkPatch**: Update multiple records
632
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:16:- **bulkDelete**: Delete multiple records
633
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:24:import { BulkOperationsPlugin } from 'json-rest-api/plugins/core/bulk-operations-plugin.js';
634
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:50: 'bulk-operations': {
635
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:52: defaultAtomic: true, // Default transaction mode
636
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:68:| `enableOptimizations` | boolean | true | Enable database-specific bulk optimizations when available |
637
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:147:## Bulk Create (bulkPost)
638
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:149:Create multiple records in a single operation. The `bulkPost` method accepts an array of JSON:API documents.
639
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:155:const bulkCreateResult = await api.scopes.authors.bulkPost({
640
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:164:console.log(inspect(bulkCreateResult));
641
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:195:const publisherResult = await api.scopes.publishers.bulkPost({
642
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:222:const partialResult = await api.scopes.authors.bulkPost({
643
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:238:// errors: [{
644
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:240:// status: 'error',
645
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:241:// error: {
646
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:256:## Bulk Update (bulkPatch)
647
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:264:const bulkUpdateResult = await api.scopes.authors.bulkPatch({
648
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:286:console.log('Updated authors:', bulkUpdateResult.meta.succeeded);
649
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:295:const bookResults = await api.scopes.books.bulkPost({
650
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:316:const updateOps = await api.scopes.books.bulkPatch({
651
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:351:const errorResult = await api.scopes.authors.bulkPatch({
652
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:360:console.log(inspect(errorResult));
653
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:361:// Shows successful updates and errors for failed operations
654
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:364:## Bulk Delete (bulkDelete)
655
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:372:const bulkDeleteResult = await api.scopes.authors.bulkDelete({
656
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:377:console.log(inspect(bulkDeleteResult));
657
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:397: await api.scopes.countries.bulkDelete({
658
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:401:} catch (error) {
659
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:402: console.log('Cannot delete:', error.message);
660
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:407:await api.scopes.books.bulkDelete({
661
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:413:await api.scopes.countries.bulkDelete({
662
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:424:const mixedResult = await api.scopes.authors.bulkDelete({
663
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:439:// errors: [
664
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:440:// { index: 1, id: '999', status: 'error', error: { code: 'REST_API_RESOURCE', message: 'Resource not found' } },
665
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:441:// { index: 3, id: '888', status: 'error', error: { code: 'REST_API_RESOURCE', message: 'Resource not found' } }
666
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:448:When using the Express plugin, bulk operations are available via HTTP endpoints:
667
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:463: console.log('API with bulk operations running on http://localhost:3000');
668
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:464:}).on('error', (err) => {
669
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:465: console.error('Failed to start server:', err);
670
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:473:POST /api/authors/bulk
671
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:484:POST /api/authors/bulk?atomic=false
672
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:490:PATCH /api/authors/bulk
673
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:504:DELETE /api/authors/bulk
674
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:526: 'bulk-operations': {
675
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:538:const result = await api.scopes.authors.bulkPost({
676
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:551:// Add a hook that runs for each bulk operation
677
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:552:api.addHook('beforePost', 'bulkTracking', {}, async ({ context, params }) => {
678
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:553: if (context.bulkOperation) {
679
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:554: console.log(`Processing bulk item ${context.bulkIndex + 1}`);
680
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:561:Implement robust error handling for bulk operations:
681
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:566: const result = await api.scopes.authors.bulkPost({
682
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:568: atomic: false // Continue on errors
683
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:574: // Handle errors if any
684
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:575: if (result.errors && result.errors.length > 0) {
685
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:576: console.error('Import errors:');
686
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:577: result.errors.forEach(error => {
687
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:578: console.error(` Row ${error.index}: ${error.error.message}`);
688
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:583: result.errors.some(e => e.index === index)
689
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:588: } catch (error) {
690
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:589: // Handle complete failure (e.g., database connection error)
691
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:590: console.error('Bulk import failed completely:', error.message);
692
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:591: throw error;
693
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:600:3. **Enable Optimizations**: The plugin uses database-specific bulk insert optimizations when available
694
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:611: const countryResult = await api.scopes.countries.bulkPost({
695
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:618: const publisherResult = await api.scopes.publishers.bulkPost({
696
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:625: const authorResult = await api.scopes.authors.bulkPost({
697
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:632: const bookResult = await api.scopes.books.bulkPost({
698
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:657: const relationshipResult = await api.scopes.book_authors.bulkPost({
699
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:676: errors: bookResult.errors || []
700
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:719:- **Three Core Operations**: bulkPost, bulkPatch, and bulkDelete
701
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:723:- **Full JSON:API Support**: Maintains compatibility with standard format
702
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:724:- **HTTP Endpoints**: RESTful API for bulk operations
703
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:726:Use bulk operations when you need to:
704
- docs/GUIDE/GUIDE_X_Bulk_Operations.md:732:Remember to consider transaction modes, error handling, and performance implications when designing your bulk operation workflows.
705
- docs/GUIDE/GUIDE_6_Positioning.md:11:5. [Position Grouping](#position-grouping)
706
- docs/GUIDE/GUIDE_6_Positioning.md:23:- **Fractional indexing**: Insert items between any two positions without updating other records
707
- docs/GUIDE/GUIDE_6_Positioning.md:26:- **Automatic positioning**: Items without explicit positions are placed appropriately
708
- docs/GUIDE/GUIDE_6_Positioning.md:31:Traditional integer-based positioning requires updating multiple records when inserting:
709
- docs/GUIDE/GUIDE_6_Positioning.md:35:UPDATE tasks SET position = position + 1 WHERE position >= 3;
710
- docs/GUIDE/GUIDE_6_Positioning.md:36:INSERT INTO tasks (title, position) VALUES ('New Task', 3);
711
- docs/GUIDE/GUIDE_6_Positioning.md:43:INSERT INTO tasks (title, position) VALUES ('New Task', 'a0m');
712
- docs/GUIDE/GUIDE_6_Positioning.md:60:import { PositioningPlugin } from './plugins/core/rest-api-positioning-plugin.js';
713
- docs/GUIDE/GUIDE_6_Positioning.md:70:// Add positioning capabilities
714
- docs/GUIDE/GUIDE_6_Positioning.md:88: // 'position' field is automatically added
715
- docs/GUIDE/GUIDE_6_Positioning.md:101:// Response includes position: "a0"
716
- docs/GUIDE/GUIDE_6_Positioning.md:109:// Gets position: "a1"
717
- docs/GUIDE/GUIDE_6_Positioning.md:117:// Gets position: "a0m" (between "a0" and "a1")
718
- docs/GUIDE/GUIDE_6_Positioning.md:122:Lists are automatically sorted by position:
719
- docs/GUIDE/GUIDE_6_Positioning.md:127:// Returns tasks in position order:
720
- docs/GUIDE/GUIDE_6_Positioning.md:130: { "id": 1, "attributes": { "title": "First task", "position": "a0" } },
721
- docs/GUIDE/GUIDE_6_Positioning.md:131: { "id": 3, "attributes": { "title": "Middle task", "position": "a0m" } },
722
- docs/GUIDE/GUIDE_6_Positioning.md:132: { "id": 2, "attributes": { "title": "Last task", "position": "a1" } }
723
- docs/GUIDE/GUIDE_6_Positioning.md:143: // Position field name (default: 'position')
724
- docs/GUIDE/GUIDE_6_Positioning.md:146: // Grouping fields - create separate position sequences per group
725
- docs/GUIDE/GUIDE_6_Positioning.md:149: // Resources to exclude from positioning
726
- docs/GUIDE/GUIDE_6_Positioning.md:158: // Default position for new items without beforeId (default: 'last')
727
- docs/GUIDE/GUIDE_6_Positioning.md:164: // Maximum position string length before rebalancing (default: 50)
728
- docs/GUIDE/GUIDE_6_Positioning.md:171:Position grouping is one of the most powerful features. It maintains separate position sequences for different combinations of field values.
729
- docs/GUIDE/GUIDE_6_Positioning.md:175:When you configure filters like `['status', 'projectId']`, the plugin creates independent position sequences for each unique combination:
730
- docs/GUIDE/GUIDE_6_Positioning.md:177:- Project 1 + Status "todo" → positions: a0, a1, a2...
731
- docs/GUIDE/GUIDE_6_Positioning.md:178:- Project 1 + Status "done" → positions: a0, a1, a2... (separate sequence!)
732
- docs/GUIDE/GUIDE_6_Positioning.md:179:- Project 2 + Status "todo" → positions: a0, a1, a2... (another separate sequence!)
733
- docs/GUIDE/GUIDE_6_Positioning.md:182:- The first item in each group gets position "a0"
734
- docs/GUIDE/GUIDE_6_Positioning.md:183:- Items can have the same position value if they're in different groups
735
- docs/GUIDE/GUIDE_6_Positioning.md:184:- Moving between groups requires explicit positioning with `beforeId`
736
- docs/GUIDE/GUIDE_6_Positioning.md:203:Each board/column combination maintains its own positions:
737
- docs/GUIDE/GUIDE_6_Positioning.md:236:**Important Behavior**: When you change a filter field value (like moving a card between columns), the item keeps its existing position value. The plugin does NOT automatically reassign positions when filter values change.
738
- docs/GUIDE/GUIDE_6_Positioning.md:244:// Result: Card moves to in-progress but KEEPS its existing position (e.g., "a0m")
739
- docs/GUIDE/GUIDE_6_Positioning.md:248:To move an item to a specific position in the new group, you MUST provide a `beforeId`:
740
- docs/GUIDE/GUIDE_6_Positioning.md:251:// Move card and position it correctly
741
- docs/GUIDE/GUIDE_6_Positioning.md:265:**Why this behavior?** The plugin cannot guess where you want the item positioned in the new group. Should it go first? Last? In the middle? You must explicitly specify the desired position.
742
- docs/GUIDE/GUIDE_6_Positioning.md:274:// Each tenant has independent position sequences
743
- docs/GUIDE/GUIDE_6_Positioning.md:275:// Tenant A's positions don't affect Tenant B's positions
744
- docs/GUIDE/GUIDE_6_Positioning.md:291:// Add at specific position
745
- docs/GUIDE/GUIDE_6_Positioning.md:295:// Note: Manual position values are ignored!
746
- docs/GUIDE/GUIDE_6_Positioning.md:296:// The plugin always calculates positions to ensure consistency
747
- docs/GUIDE/GUIDE_6_Positioning.md:298:{ "name": "New item", "position": "a0abc" } // 'position' will be recalculated!
748
- docs/GUIDE/GUIDE_6_Positioning.md:312:// Update other fields without changing position
749
- docs/GUIDE/GUIDE_6_Positioning.md:320:// Item moves to 'done' group but KEEPS its position value!
750
- docs/GUIDE/GUIDE_6_Positioning.md:326:The plugin works with both JSON:API and simplified formats:
751
- docs/GUIDE/GUIDE_6_Positioning.md:337:// JSON:API format
752
- docs/GUIDE/GUIDE_6_Positioning.md:355:The plugin NEVER changes an item's position unless you explicitly request it with `beforeId`. This means:
753
- docs/GUIDE/GUIDE_6_Positioning.md:357:- Changing filter fields (status, category, etc.) does NOT reposition the item
754
- docs/GUIDE/GUIDE_6_Positioning.md:358:- The item keeps its position value when moving between groups
755
- docs/GUIDE/GUIDE_6_Positioning.md:359:- You must provide `beforeId` to position items in their new group
756
- docs/GUIDE/GUIDE_6_Positioning.md:363:Each combination of filter values creates a completely separate position space:
757
- docs/GUIDE/GUIDE_6_Positioning.md:366:// These items can all have position "a0" because they're in different groups:
758
- docs/GUIDE/GUIDE_6_Positioning.md:367:item1: { projectId: 1, status: 'todo', position: 'a0' }
759
- docs/GUIDE/GUIDE_6_Positioning.md:368:item2: { projectId: 1, status: 'done', position: 'a0' } // Different status
760
- docs/GUIDE/GUIDE_6_Positioning.md:369:item3: { projectId: 2, status: 'todo', position: 'a0' } // Different project
761
- docs/GUIDE/GUIDE_6_Positioning.md:374:The `beforeId` only works within the same position group:
762
- docs/GUIDE/GUIDE_6_Positioning.md:388:The plugin always calculates positions to ensure consistency:
763
- docs/GUIDE/GUIDE_6_Positioning.md:391:// This position value will be ignored:
764
- docs/GUIDE/GUIDE_6_Positioning.md:395: "position": "zzz" // Ignored! Plugin calculates actual position
765
- docs/GUIDE/GUIDE_6_Positioning.md:401:Null values in filter fields create their own position group:
766
- docs/GUIDE/GUIDE_6_Positioning.md:404:// These are THREE different position groups:
767
- docs/GUIDE/GUIDE_6_Positioning.md:461:async function addToPlaylist(songId, position = null) {
768
- docs/GUIDE/GUIDE_6_Positioning.md:466: beforeId: position // null = add to end
769
- docs/GUIDE/GUIDE_6_Positioning.md:491:The plugin uses fractional indexing to generate position keys that can be infinitely subdivided:
770
- docs/GUIDE/GUIDE_6_Positioning.md:493:1. **Initial positions**: First item gets "a0", second gets "a1", etc.
771
- docs/GUIDE/GUIDE_6_Positioning.md:508:3. **Determines if positioning is needed**:
772
- docs/GUIDE/GUIDE_6_Positioning.md:509: - For POST: Always calculates position
773
- docs/GUIDE/GUIDE_6_Positioning.md:511: - Changing filter fields alone does NOT trigger repositioning
774
- docs/GUIDE/GUIDE_6_Positioning.md:512:4. **Query database** for items in the same position group (based on filter fields)
775
- docs/GUIDE/GUIDE_6_Positioning.md:513:5. **Calculate position**:
776
- docs/GUIDE/GUIDE_6_Positioning.md:518:6. **Store position** in the position field
777
- docs/GUIDE/GUIDE_6_Positioning.md:519:7. **Save record** with calculated position
778
- docs/GUIDE/GUIDE_6_Positioning.md:521:**Key Insight**: The position is calculated relative to other items in the same "position group" (items with matching filter field values). An item with `status: 'todo'` has no position relationship with items where `status: 'done'`.
779
- docs/GUIDE/GUIDE_6_Positioning.md:528:CREATE INDEX idx_tasks_positioning ON tasks(status, projectId, position);
780
- docs/GUIDE/GUIDE_6_Positioning.md:533:- Finding specific positions for insertion
781
- docs/GUIDE/GUIDE_6_Positioning.md:540:If you have existing integer positions, you can migrate gradually:
782
- docs/GUIDE/GUIDE_6_Positioning.md:543:// 1. Add the plugin (it works alongside existing positions)
783
- docs/GUIDE/GUIDE_6_Positioning.md:548:// 2. New items will get fractional positions
784
- docs/GUIDE/GUIDE_6_Positioning.md:549:// 3. Existing integer positions still work (treated as strings)
785
- docs/GUIDE/GUIDE_6_Positioning.md:570:The plugin automatically adds the position field to schemas:
786
- docs/GUIDE/GUIDE_6_Positioning.md:581:// 'position' field is automatically added to the schema
787
- docs/GUIDE/GUIDE_6_Positioning.md:591:-- For ungrouped positioning
788
- docs/GUIDE/GUIDE_6_Positioning.md:592:CREATE INDEX ON items(position);
789
- docs/GUIDE/GUIDE_6_Positioning.md:594:-- For grouped positioning
790
- docs/GUIDE/GUIDE_6_Positioning.md:595:CREATE INDEX ON items(status, projectId, position);
791
- docs/GUIDE/GUIDE_6_Positioning.md:601:- **Inserting items**: O(log n) to find position + O(1) to insert
792
- docs/GUIDE/GUIDE_6_Positioning.md:602:- **Moving items**: O(log n) to find positions + O(1) to update
793
- docs/GUIDE/GUIDE_6_Positioning.md:608:- Starting positions: "a0", "a1" (2 characters)
794
- docs/GUIDE/GUIDE_6_Positioning.md:616:2. **Avoid manual positions** unless migrating data
795
- docs/GUIDE/GUIDE_6_Positioning.md:617:3. **Let the plugin handle positioning** for consistency
796
- docs/GUIDE/GUIDE_6_Positioning.md:618:4. **Monitor position lengths** in high-activity systems
797
- docs/GUIDE/GUIDE_6_Positioning.md:626:- Verify the position field contains valid fractional keys
798
- docs/GUIDE/GUIDE_6_Positioning.md:627:- Ensure you're querying within the correct position group
799
- docs/GUIDE/GUIDE_6_Positioning.md:628:- Remember: position values are strings, sorted lexicographically ("a10" comes before "a2"!)
800
- docs/GUIDE/GUIDE_6_Positioning.md:631:- The position field must exist in your schema
801
- docs/GUIDE/GUIDE_6_Positioning.md:633:- The plugin will throw an error if the field is missing
802
- docs/GUIDE/GUIDE_6_Positioning.md:634:- Look for plugin initialization errors in logs
803
- docs/GUIDE/GUIDE_6_Positioning.md:637:- Ensure the target item exists in the same position group
804
- docs/GUIDE/GUIDE_6_Positioning.md:640:- Note: You cannot position relative to items in different groups
805
- docs/GUIDE/GUIDE_6_Positioning.md:642:**Items appear in wrong position after moving between groups**
806
- docs/GUIDE/GUIDE_6_Positioning.md:643:- This is expected behavior! Items keep their position when filter values change
807
- docs/GUIDE/GUIDE_6_Positioning.md:649:- Monitor position string lengths
808
- docs/GUIDE/GUIDE_6_Positioning.md:654:Enable debug logging to see position calculations:
809
- docs/GUIDE/GUIDE_6_Positioning.md:665:For advanced use cases, you can work directly with positions:
810
- docs/GUIDE/GUIDE_6_Positioning.md:670:// Generate a position between two items
811
- docs/GUIDE/GUIDE_6_Positioning.md:673:// Generate first position
812
- docs/GUIDE/GUIDE_6_Positioning.md:676:// Generate last position after 'z5'
813
- docs/GUIDE/GUIDE_6_Positioning.md:682:The Positioning Plugin provides a production-ready solution for maintaining custom order in your REST API resources. With fractional indexing and position grouping, it handles complex ordering requirements while maintaining excellent performance and avoiding conflicts.
814
- docs/GUIDE/GUIDE_6_Positioning.md:688:- **Grouped positioning** - Separate sequences per category
815
- docs/GUIDE/GUIDE_6_Positioning.md:692:The plugin integrates seamlessly with the REST API plugin ecosystem, requiring minimal configuration while providing powerful positioning capabilities for modern applications.
816
- docs/GUIDE/GUIDE_X_SocketIO.md:31:The Socket.IO plugin is included in json-rest-api core plugins. To use it, you need to:
817
- docs/GUIDE/GUIDE_X_SocketIO.md:35:npm install socket.io @socket.io/redis-adapter redis
818
- docs/GUIDE/GUIDE_X_SocketIO.md:44:import { SocketIOPlugin } from 'json-rest-api/plugins/socketio';
819
- docs/GUIDE/GUIDE_X_SocketIO.md:59:const server = app.listen(3000).on('error', (err) => {
820
- docs/GUIDE/GUIDE_X_SocketIO.md:60: console.error('Failed to start server:', err);
821
- docs/GUIDE/GUIDE_X_SocketIO.md:66: path: '/socket.io', // Socket.IO path (default: '/socket.io')
822
- docs/GUIDE/GUIDE_X_SocketIO.md:67: cors: { // CORS configuration
823
- docs/GUIDE/GUIDE_X_SocketIO.md:84:| `path` | string | '/socket.io' | URL path for Socket.IO endpoint |
824
- docs/GUIDE/GUIDE_X_SocketIO.md:85:| `cors` | object | `{ origin: '*', methods: ['GET', 'POST'] }` | CORS configuration |
825
- docs/GUIDE/GUIDE_X_SocketIO.md:117:socket.emit('subscription.update', {
826
- docs/GUIDE/GUIDE_X_SocketIO.md:166:socket.emit('subscribe', {
827
- docs/GUIDE/GUIDE_X_SocketIO.md:180:// In a transaction
828
- docs/GUIDE/GUIDE_X_SocketIO.md:181:const trx = await knex.transaction();
829
- docs/GUIDE/GUIDE_X_SocketIO.md:186: transaction: trx
830
- docs/GUIDE/GUIDE_X_SocketIO.md:193:} catch (error) {
831
- docs/GUIDE/GUIDE_X_SocketIO.md:194: await trx.rollback();
832
- docs/GUIDE/GUIDE_X_SocketIO.md:225:socket.emit('subscribe', {
833
- docs/GUIDE/GUIDE_X_SocketIO.md:263: return title.includes(search) ||
834
- docs/GUIDE/GUIDE_X_SocketIO.md:264: desc.includes(search) ||
835
- docs/GUIDE/GUIDE_X_SocketIO.md:265: tags.includes(search);
836
- docs/GUIDE/GUIDE_X_SocketIO.md:302: const { user_id, include_private } = value;
837
- docs/GUIDE/GUIDE_X_SocketIO.md:304: if (!include_private) {
838
- docs/GUIDE/GUIDE_X_SocketIO.md:311: const { user_id, include_private } = value;
839
- docs/GUIDE/GUIDE_X_SocketIO.md:314: return include_private && record.shared_with?.includes(user_id);
840
- docs/GUIDE/GUIDE_X_SocketIO.md:326:socket.emit('subscribe', {
841
- docs/GUIDE/GUIDE_X_SocketIO.md:334:// Returns error:
842
- docs/GUIDE/GUIDE_X_SocketIO.md:336: error: {
843
- docs/GUIDE/GUIDE_X_SocketIO.md:357:const socket = io('http://localhost:3000', {
844
- docs/GUIDE/GUIDE_X_SocketIO.md:364:socket.on('connect_error', (error) => {
845
- docs/GUIDE/GUIDE_X_SocketIO.md:365: console.error('Authentication failed:', error.message);
846
- docs/GUIDE/GUIDE_X_SocketIO.md:381: return auth.roles?.includes('admin');
847
- docs/GUIDE/GUIDE_X_SocketIO.md:419:// Now ALL subscriptions automatically include workspace filter
848
- docs/GUIDE/GUIDE_X_SocketIO.md:420:socket.emit('subscribe', {
849
- docs/GUIDE/GUIDE_X_SocketIO.md:443: if (!auth.roles?.includes('admin')) {
850
- docs/GUIDE/GUIDE_X_SocketIO.md:495:Each socket can have multiple subscriptions with different filters:
851
- docs/GUIDE/GUIDE_X_SocketIO.md:499:const sub1 = await subscribeToResource(socket, {
852
- docs/GUIDE/GUIDE_X_SocketIO.md:504:const sub2 = await subscribeToResource(socket, {
853
- docs/GUIDE/GUIDE_X_SocketIO.md:509:const sub3 = await subscribeToResource(socket, {
854
- docs/GUIDE/GUIDE_X_SocketIO.md:515:socket.emit('unsubscribe', {
855
- docs/GUIDE/GUIDE_X_SocketIO.md:522:While notifications don't include data, subscriptions can store include/fields preferences:
856
- docs/GUIDE/GUIDE_X_SocketIO.md:525:// Subscribe with preferred includes and fields
857
- docs/GUIDE/GUIDE_X_SocketIO.md:526:socket.emit('subscribe', {
858
- docs/GUIDE/GUIDE_X_SocketIO.md:529: include: ['author', 'comments.user'],
859
- docs/GUIDE/GUIDE_X_SocketIO.md:538:socket.on('subscription.update', async (notification) => {
860
- docs/GUIDE/GUIDE_X_SocketIO.md:541: 'include=author,comments.user&' +
861
- docs/GUIDE/GUIDE_X_SocketIO.md:557:socket.on('subscription.created', (response) => {
862
- docs/GUIDE/GUIDE_X_SocketIO.md:562:socket.on('connect', async () => {
863
- docs/GUIDE/GUIDE_X_SocketIO.md:565: socket,
864
- docs/GUIDE/GUIDE_X_SocketIO.md:571: console.error(`Failed to restore ${failed.length} subscriptions`);
865
- docs/GUIDE/GUIDE_X_SocketIO.md:576:async function restoreSubscriptions(socket, subscriptions) {
866
- docs/GUIDE/GUIDE_X_SocketIO.md:578: socket.emit('restore-subscriptions',
867
- docs/GUIDE/GUIDE_X_SocketIO.md:588:The plugin provides detailed error information:
868
- docs/GUIDE/GUIDE_X_SocketIO.md:591:socket.on('subscription.error', (error) => {
869
- docs/GUIDE/GUIDE_X_SocketIO.md:592: switch (error.code) {
870
- docs/GUIDE/GUIDE_X_SocketIO.md:594: console.error(`Resource type '${error.resource}' doesn't exist`);
871
- docs/GUIDE/GUIDE_X_SocketIO.md:598: console.error('You lack permission to subscribe to this resource');
872
- docs/GUIDE/GUIDE_X_SocketIO.md:602: console.error('Filter validation failed:', error.details);
873
- docs/GUIDE/GUIDE_X_SocketIO.md:606: console.error(`Filter requires 'filterRecord' for WebSocket support`);
874
- docs/GUIDE/GUIDE_X_SocketIO.md:610: console.error('Resource does not have searchSchema defined');
875
- docs/GUIDE/GUIDE_X_SocketIO.md:621:import { io } from 'socket.io-client';
876
- docs/GUIDE/GUIDE_X_SocketIO.md:624:const socket = io('http://localhost:3000', {
877
- docs/GUIDE/GUIDE_X_SocketIO.md:631:socket.on('connect', () => {
878
- docs/GUIDE/GUIDE_X_SocketIO.md:635:socket.on('connect_error', (error) => {
879
- docs/GUIDE/GUIDE_X_SocketIO.md:636: console.error('Connection failed:', error.message);
880
- docs/GUIDE/GUIDE_X_SocketIO.md:639:socket.on('disconnect', (reason) => {
881
- docs/GUIDE/GUIDE_X_SocketIO.md:648:async function subscribeToResource(socket, options) {
882
- docs/GUIDE/GUIDE_X_SocketIO.md:650: socket.emit('subscribe', options, (response) => {
883
- docs/GUIDE/GUIDE_X_SocketIO.md:651: if (response.error) {
884
- docs/GUIDE/GUIDE_X_SocketIO.md:652: reject(response.error);
885
- docs/GUIDE/GUIDE_X_SocketIO.md:662: const subscription = await subscribeToResource(socket, {
886
- docs/GUIDE/GUIDE_X_SocketIO.md:671:} catch (error) {
887
- docs/GUIDE/GUIDE_X_SocketIO.md:672: console.error('Subscription failed:', error);
888
- docs/GUIDE/GUIDE_X_SocketIO.md:680:socket.on('subscription.update', async (notification) => {
889
- docs/GUIDE/GUIDE_X_SocketIO.md:739: this.socket = io(apiUrl, {
890
- docs/GUIDE/GUIDE_X_SocketIO.md:747: this.socket.on('connect', () => {
891
- docs/GUIDE/GUIDE_X_SocketIO.md:752: this.socket.on('subscription.update', (notification) => {
892
- docs/GUIDE/GUIDE_X_SocketIO.md:756: this.socket.on('subscription.created', (response) => {
893
- docs/GUIDE/GUIDE_X_SocketIO.md:763: this.socket.emit('subscribe', {
894
- docs/GUIDE/GUIDE_X_SocketIO.md:768: if (response.error) {
895
- docs/GUIDE/GUIDE_X_SocketIO.md:769: reject(response.error);
896
- docs/GUIDE/GUIDE_X_SocketIO.md:829: this.socket.emit('restore-subscriptions',
897
- docs/GUIDE/GUIDE_X_SocketIO.md:832: if (response.error) {
898
- docs/GUIDE/GUIDE_X_SocketIO.md:833: console.error('Failed to restore subscriptions:', response.error);
899
- docs/GUIDE/GUIDE_X_SocketIO.md:860: include: 'author,comments'
900
- docs/GUIDE/GUIDE_X_SocketIO.md:871:Each socket is limited to 100 subscriptions to prevent memory exhaustion:
901
- docs/GUIDE/GUIDE_X_SocketIO.md:875:socket.emit('subscribe', { resource: 'posts' }, (response) => {
902
- docs/GUIDE/GUIDE_X_SocketIO.md:876: if (response.error?.code === 'SUBSCRIPTION_LIMIT_EXCEEDED') {
903
- docs/GUIDE/GUIDE_X_SocketIO.md:877: console.error('Too many active subscriptions');
904
- docs/GUIDE/GUIDE_X_SocketIO.md:905:socket.on('subscription.update', (notification) => {
905
- docs/GUIDE/GUIDE_X_SocketIO.md:938:- Are you in a transaction that hasn't committed?
906
- docs/GUIDE/GUIDE_X_SocketIO.md:940:**2. "UNSUPPORTED_FILTER" errors**
907
- docs/GUIDE/GUIDE_X_SocketIO.md:945:// ❌ This will error for WebSocket
908
- docs/GUIDE/GUIDE_X_SocketIO.md:989:localStorage.debug = 'socket.io-client:*';
909
- docs/GUIDE/GUIDE_X_SocketIO.md:997: const socket = io('http://localhost:3000', {
910
- docs/GUIDE/GUIDE_X_SocketIO.md:1002: socket.on('connect', async () => {
911
- docs/GUIDE/GUIDE_X_SocketIO.md:1006: socket.emit('subscribe', {
912
- docs/GUIDE/GUIDE_X_SocketIO.md:1010: if (response.error) {
913
- docs/GUIDE/GUIDE_X_SocketIO.md:1011: console.error('✗ Subscription failed:', response.error);
914
- docs/GUIDE/GUIDE_X_SocketIO.md:1012: reject(response.error);
915
- docs/GUIDE/GUIDE_X_SocketIO.md:1017: socket.once('subscription.update', (notification) => {
916
- docs/GUIDE/GUIDE_X_SocketIO.md:1019: socket.close();
917
- docs/GUIDE/GUIDE_X_SocketIO.md:1029: socket.on('connect_error', (error) => {
918
- docs/GUIDE/GUIDE_X_SocketIO.md:1030: console.error('✗ Connection failed:', error.message);
919
- docs/GUIDE/GUIDE_X_SocketIO.md:1031: reject(error);
920
- docs/GUIDE/GUIDE_3_Field_Transformations.md:349: // - context: request context with knex, transaction, etc.
921
- docs/GUIDE/GUIDE_3_Field_Transformations.md:361:4. **Sparse Fieldsets**: Work seamlessly with JSON:API sparse fieldsets
922
- docs/GUIDE/GUIDE_3_Field_Transformations.md:533: // Note: cost, supplierApiKey not included
923
- docs/GUIDE/GUIDE_3_Field_Transformations.md:594:Computed fields are virtual fields that don't exist in your database but are calculated on-the-fly from other fields. They're computed after the database load, every time they're requested, and can depend on other fields (including hidden ones). Computed fields work seamlessly with sparse fieldsets and are calculated for both main resources and included resources.
924
- docs/GUIDE/GUIDE_3_Field_Transformations.md:682:// Note: 'cost' is not included (normallyHidden)
925
- docs/GUIDE/GUIDE_3_Field_Transformations.md:710:When you request a computed field via sparse fieldsets, the system automatically fetches its dependencies:
926
- docs/GUIDE/GUIDE_3_Field_Transformations.md:714:const sparseProduct = await api.resources.products.get({
927
- docs/GUIDE/GUIDE_3_Field_Transformations.md:720:console.log('Product with sparse fields:', sparseProduct);
928
- docs/GUIDE/GUIDE_3_Field_Transformations.md:728:console.log('Product with sparse fields includig cost:', productWithCost);
929
- docs/GUIDE/GUIDE_3_Field_Transformations.md:734:Product with sparse fields: {
930
- docs/GUIDE/GUIDE_3_Field_Transformations.md:741:Product with sparse fields includig cost: {
931
- docs/GUIDE/GUIDE_3_Field_Transformations.md:752:Computed fields work seamlessly with included resources:
932
- docs/GUIDE/GUIDE_3_Field_Transformations.md:759: include: ['reviews']
933
- docs/GUIDE/GUIDE_3_Field_Transformations.md:764:// Use sparse fieldsets on included resources
934
- docs/GUIDE/GUIDE_3_Field_Transformations.md:768: include: ['reviews'],
935
- docs/GUIDE/GUIDE_3_Field_Transformations.md:833:Computed fields handle errors gracefully:
936
- docs/GUIDE/GUIDE_3_Field_Transformations.md:848:console.log('Fetched review (with error in helpfulness score):', fetchedReviewWithError
937
- docs/GUIDE/GUIDE_3_Field_Transformations.md:854:Fetched review (with error in helpfulness score): {
938
- docs/GUIDE/GUIDE_3_Field_Transformations.md:872:3. **Works with Sparse Fieldsets** - You can request computed fields just like regular fields using sparse fieldsets.
939
- docs/GUIDE/GUIDE_3_Field_Transformations.md:876:5. **Included Resources** - Computed fields are calculated for all resources, whether they're the main resource or included via relationships.
940
- docs/GUIDE/GUIDE_3_Field_Transformations.md:878:6. **Error Handling** - If a computation fails, the field is set to `null` and an error is logged, but the request continues.
941
- docs/GUIDE/GUIDE_3_Field_Transformations.md:998: queryParams: { include: ['posts'] }
942
- docs/GUIDE/GUIDE_3_Field_Transformations.md:1069: queryParams: { include: ['posts'] }
943
- docs/GUIDE/GUIDE_3_Field_Transformations.md:1099: include: ['author'],
944
- docs/GUIDE/GUIDE_3_Field_Transformations.md:1105:// Both posts and included authors have getters applied
945
- docs/mini-guides/file-uploads-complete.md:110:}).on('error', (err) => {
946
- docs/mini-guides/file-uploads-complete.md:111: console.error('Failed to start server:', err);
947
- docs/mini-guides/file-uploads-complete.md:132:The example includes a simple HTML form for testing file uploads without needing external tools.
948
- docs/GUIDE/GUIDE_X_File_Uploads.md:106:Storage adapters handle where and how files are saved. The library includes two built-in adapters.
949
- docs/GUIDE/GUIDE_X_File_Uploads.md:140:**Note**: The included S3Storage is a mock implementation for demonstration. For production use, you'll need to implement the actual AWS SDK calls.
950
- docs/GUIDE/GUIDE_X_File_Uploads.md:321: return req.headers['content-type']?.includes('multipart/form-data');
951
- docs/GUIDE/GUIDE_X_File_Uploads.md:393:When validation fails, you'll get appropriate error responses:
952
- docs/GUIDE/GUIDE_X_File_Uploads.md:397: "errors": [{
953
- docs/GUIDE/GUIDE_X_File_Uploads.md:460:app.listen(3000).on('error', (err) => {
954
- docs/GUIDE/GUIDE_X_File_Uploads.md:461: console.error('Failed to start server:', err);
955
- docs/GUIDE/GUIDE_X_File_Uploads.md:613:#### 4. "File too large" errors
956
- docs/GUIDE/GUIDE_X_File_Uploads.md:650:Use the included example to test your setup:
957
- docs/GUIDE/GUIDE_X_File_Uploads.md:716:7. **Serve files with proper headers** - Use Content-Disposition for downloads
958
- docs/GUIDE/GUIDE_X_URL_Management.md:5:The JSON REST API automatically generates appropriate URLs for all resources in JSON:API responses. URLs are calculated **per-request** based on the incoming request headers, ensuring correct URLs in all deployment scenarios without configuration.
959
- docs/GUIDE/GUIDE_X_URL_Management.md:93: if (host?.includes('tenant-a')) {
960
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:9:3. [JSON:API Response Format](#jsonapi-response-format)
961
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:18:Storage plugins are the bridge between the JSON REST API library and your data source. The REST API plugin handles all the HTTP routing, validation, permissions, and JSON:API formatting, while your storage plugin is responsible for actually storing and retrieving data.
962
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:35:4. Your helper returns data in JSON:API format
963
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:51: // - context.schemaInfo.idProperty: string - Primary key field name
964
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:52: // - context.db: any - Database connection (if using transactions)
965
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:59:Retrieve a single resource with full JSON:API features.
966
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:66: // - context.queryParams.include: string[] - Related resources to include
967
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:70: // Returns: JSON:API document with single resource
968
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:73: // included: [...] // if includes requested
969
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:85: // Returns: JSON:API resource object or null
970
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:104: // - context.queryParams.include: string[] - Related resources
971
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:107: // Returns: JSON:API document with resource array
972
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:110: // included: [...],
973
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:123: // - context.inputRecord: JSON:API document with new resource
974
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:137: // - context.inputRecord: JSON:API document with full resource
975
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:140: // Returns: void (throws error if not found when updating)
976
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:151: // - context.inputRecord: JSON:API document with partial updates
977
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:153: // Returns: void (throws error if not found)
978
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:165: // Returns: { success: true } (throws error if not found)
979
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:169:## JSON:API Response Format
980
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:171:Your storage plugin must return data in proper JSON:API format. Here are the key structures:
981
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:227: included: [
982
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:284: const { RestApiResourceError } = await import('../../lib/rest-api-errors.js');
983
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:292: // Build JSON:API response
984
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:322: // Handle includes
985
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:323: const included = [];
986
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:324: if (context.queryParams.include?.length > 0) {
987
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:325: for (const includePath of context.queryParams.include) {
988
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:326: const relName = includePath.split('.')[0];
989
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:338: included.push({
990
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:349: // Apply sparse fieldsets
991
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:353: Object.entries(data.attributes).filter(([key]) => fields.includes(key))
992
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:359: ...(included.length > 0 && { included })
993
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:377: // Only include belongsTo relationships for minimal
994
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:405: if (field.includes('.')) {
995
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:475: // Handle includes
996
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:476: const included = [];
997
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:477: if (context.queryParams.include?.length > 0) {
998
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:478: const includedIds = new Set();
999
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:481: for (const includePath of context.queryParams.include) {
1000
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:482: const relName = includePath.split('.')[0];
1001
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:492: if (!includedIds.has(key)) {
1002
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:493: includedIds.add(key);
1003
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:497: included.push({
1004
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:529: ...(included.length > 0 && { included }),
1005
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:576: const { RestApiResourceError } = await import('../../lib/rest-api-errors.js');
1006
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:615: const { RestApiResourceError } = await import('../../lib/rest-api-errors.js');
1007
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:654: const { RestApiResourceError } = await import('../../lib/rest-api-errors.js');
1008
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:702: const error = await response.json().catch(() => ({ errors: [{ title: 'Request failed' }] }));
1009
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:703: const { RestApiResourceError } = await import('../../lib/rest-api-errors.js');
1010
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:705: error.errors?.[0]?.title || 'Remote API error',
1011
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:707: subtype: response.status === 404 ? 'not_found' : 'remote_error',
1012
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:709: errors: error.errors
1013
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:722: } catch (error) {
1014
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:723: if (error.statusCode === 404) return false;
1015
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:724: throw error;
1016
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:732: // Add include parameter
1017
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:733: if (context.queryParams.include?.length > 0) {
1018
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:734: queryParams.set('include', context.queryParams.include.join(','));
1019
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:737: // Add sparse fieldsets
1020
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:779: // Add includes
1021
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:780: if (context.queryParams.include?.length > 0) {
1022
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:781: queryParams.set('include', context.queryParams.include.join(','));
1023
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:784: // Add sparse fieldsets
1024
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:799: included: response.included,
1025
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:849:If your storage supports transactions, the `context.db` parameter will automatically contain the transaction when one is active:
1026
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:853: // context.db is automatically the transaction if one is active,
1027
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:854: // or the base connection if not in a transaction
1028
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:857: // For storage that supports transactions:
1029
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:858: if (context.transaction) {
1030
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:859: // We're in a transaction - ensure all operations use it
1031
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:862: // No transaction - use regular connection
1032
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:881: // Filter attributes to only include requested fields
1033
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:883: Object.entries(data.attributes).filter(([key]) => fields.includes(key))
1034
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:887: // The REST API plugin will handle nested field filtering for included resources
1035
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:893:The REST API plugin handles computed fields, but you may need to ensure dependency fields are included:
1036
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:897:// Always include these fields even if not explicitly requested
1037
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:911:Always use the proper error classes:
1038
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:914:import { RestApiResourceError, RestApiValidationError } from '../../lib/rest-api-errors.js';
1039
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:916:// For not found errors
1040
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:923:// For validation errors
1041
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:1000:JSON:API requires IDs to be strings, but your storage might use numbers:
1042
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:1012:Relationships must follow the JSON:API format exactly:
1043
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:1042:Errors should include proper subtypes:
1044
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:1047:'validation_error' - Input validation failed
1045
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:1054:Always include pagination metadata for queries:
1046
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:1067:When returning included resources, avoid duplicates:
1047
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:1070:const includedMap = new Map();
1048
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:1072:includedMap.set(`${type}:${id}`, resource);
1049
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:1073:const included = Array.from(includedMap.values());
1050
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:1080:- Use bulk operations where possible
1051
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:1090:- idProperty: Primary key field name (might not be 'id')
1052
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:1098:Creating a custom storage plugin gives you complete control over how data is stored and retrieved while leveraging all the features of the JSON REST API library. The key is to properly implement the 8 required helpers and ensure all responses follow the JSON:API specification.
1053
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:1102:- Always return proper JSON:API formatted responses
1054
- docs/GUIDE/GUIDE_5_Non-Db_Resources.md:1103:- Use the provided error classes for consistency
1055
- docs/GUIDE/GUIDE_X_Multihome.md:22:- **Automatic Query Filtering**: Every database query automatically includes a WHERE clause for the tenant ID
1056
- docs/GUIDE/GUIDE_X_Multihome.md:44:Every database query is automatically modified to include the tenant filter:
1057
- docs/GUIDE/GUIDE_X_Multihome.md:51:SELECT * FROM posts WHERE status = 'published' AND multihome_id = 'mobily'
1058
- docs/GUIDE/GUIDE_X_Multihome.md:64:{ "title": "My Post", "content": "...", "multihome_id": "mobily" }
1059
- docs/GUIDE/GUIDE_X_Multihome.md:76:import { MultiHomePlugin } from './plugins/core/multihome-plugin.js';
1060
- docs/GUIDE/GUIDE_X_Multihome.md:124:| `field` | string | `'multihome_id'` | The database field name for tenant ID |
1061
- docs/GUIDE/GUIDE_X_Multihome.md:176: if (subdomain && !['www', 'api'].includes(subdomain)) {
1062
- docs/GUIDE/GUIDE_X_Multihome.md:296:// Result: 404 Not Found (not a security error message)
1063
- docs/GUIDE/GUIDE_X_Multihome.md:320:log.error('Multihome security violation attempt', {
1064
- docs/GUIDE/GUIDE_X_Multihome.md:335:// Error: No multihome context available - cannot execute query
1065
- docs/GUIDE/GUIDE_X_Multihome.md:385:const config = api.multihome.getConfig();
1066
- docs/GUIDE/GUIDE_X_Multihome.md:398:The plugin sets these variables accessible via `api.vars.multihome`:
1067
- docs/GUIDE/GUIDE_X_Multihome.md:425:#### 1. "No multihome context available"
1068
- docs/GUIDE/GUIDE_X_Multihome.md:447:**Solution**: Don't include tenant_id in your requests - it's set automatically.
1069
- docs/GUIDE/GUIDE_X_Multihome.md:457: console.log('Current tenant:', context.auth?.multihome_id);
1070
- docs/GUIDE/GUIDE_X_Multihome.md:481:Always include the tenant field in your schemas:
1071
- docs/GUIDE/GUIDE_X_Multihome.md:489: tenant_id: { type: 'string', required: true } // Always include
1072
- docs/GUIDE/GUIDE_X_Multihome.md:576:import { MultiHomePlugin } from './plugins/core/multihome-plugin.js';
1073
- docs/GUIDE/GUIDE_X_Multihome.md:617: if (subdomain && !['www', 'api', 'app'].includes(subdomain)) {
1074
- docs/GUIDE/GUIDE_X_Multihome.md:653:}).on('error', (err) => {
1075
- docs/GUIDE/GUIDE_X_Multihome.md:654: console.error('Failed to start server:', err);