json-rest-api 1.0.28 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +10 -670
- package/LICENSE-GPL-3.0 +674 -0
- package/LICENSE-MIT +21 -0
- package/README.md +39 -30
- package/docs/API.md +746 -1273
- package/docs/GUIDE/01-getting-started.md +338 -0
- package/docs/GUIDE/02-resources-and-relationships.md +33 -0
- package/docs/GUIDE/03-running-example.md +87 -0
- package/docs/GUIDE/04-creating-and-querying.md +181 -0
- package/docs/GUIDE/05-belongs-to.md +177 -0
- package/docs/GUIDE/06-has-many.md +184 -0
- package/docs/GUIDE/07-polymorphic-relationships.md +191 -0
- package/docs/GUIDE/08-many-to-many.md +184 -0
- package/docs/GUIDE/09-pagination-and-sorting.md +149 -0
- package/docs/GUIDE/10-put-and-patch.md +174 -0
- package/docs/GUIDE/11-relationship-endpoints.md +174 -0
- package/docs/GUIDE/12-field-transformations.md +287 -0
- package/docs/GUIDE/13-hooks-and-lifecycle.md +479 -0
- package/docs/GUIDE/14-custom-resource-methods.md +125 -0
- package/docs/GUIDE/15-query-projections.md +266 -0
- package/docs/GUIDE/{GUIDE_X_Autofiltering.md → 16-autofiltering.md} +77 -15
- package/docs/GUIDE/{GUIDE_X_Row_Policies.md → 17-row-policies.md} +137 -44
- package/docs/GUIDE/18-labels.md +86 -0
- package/docs/GUIDE/19-managed-transactions.md +179 -0
- package/docs/GUIDE/20-transaction-outcomes.md +207 -0
- package/docs/GUIDE/21-schema-and-migrations.md +634 -0
- package/docs/GUIDE/22-version-field-migrations.md +208 -0
- package/docs/GUIDE/23-fastify.md +179 -0
- package/docs/GUIDE/24-cors.md +172 -0
- package/docs/GUIDE/{GUIDE_X_URL_Management.md → 25-url-management.md} +7 -1
- package/docs/GUIDE/26-file-uploads.md +278 -0
- package/docs/GUIDE/27-bulk-operations.md +277 -0
- package/docs/GUIDE/28-socketio.md +325 -0
- package/docs/GUIDE/29-writing-plugins.md +278 -0
- package/docs/GUIDE/30-backend-capabilities.md +100 -0
- package/docs/GUIDE/31-positioning.md +181 -0
- package/docs/GUIDE/{GUIDE_Y_Appendices.md → 32-date-and-time.md} +90 -31
- package/docs/GUIDE/33-migrating-to-v2.md +3827 -0
- package/docs/GUIDE/index.md +51 -59
- package/docs/QUICKSTART.md +120 -287
- package/docs/architecture.md +75 -0
- package/docs/contributing.md +178 -0
- package/index.d.ts +36 -0
- package/index.js +1 -22
- package/lib/enhanced-logger.js +91 -85
- package/lib/error-context.js +445 -5
- package/lib/error-formatter.js +219 -59
- package/lib/knex-transaction.js +64 -0
- package/lib/logger-types.d.ts +22 -0
- package/lib/missing-package.js +4 -0
- package/lib/rest-api-errors.js +103 -8
- package/lib/runtime/json-rest-api.js +172 -0
- package/lib/transaction-types.d.ts +59 -0
- package/package.json +97 -27
- package/plugins/core/bulk-operations-plugin.js +191 -184
- package/plugins/core/connectors/express-plugin.js +148 -219
- package/plugins/core/connectors/fastify-plugin.js +125 -82
- package/plugins/core/connectors/lib/busboy-detector.js +74 -156
- package/plugins/core/connectors/lib/connector-core.js +144 -20
- package/plugins/core/connectors/lib/formidable-detector.js +63 -120
- package/plugins/core/connectors/lib/http-validators.js +77 -0
- package/plugins/core/connectors/lib/multipart-helpers.js +18 -0
- package/plugins/core/connectors/lib/request-helpers.js +10 -1
- package/plugins/core/connectors/lib/transport-http-helpers.js +89 -8
- package/plugins/core/connectors/lib/transport-route-schemas.js +7 -4
- package/plugins/core/file-handling-plugin.js +87 -174
- package/plugins/core/lib/anyapi/anyapi-registry.js +213 -164
- package/plugins/core/lib/anyapi/canonical-link-store.js +507 -0
- package/plugins/core/lib/anyapi/canonical-relationship-reader.js +598 -0
- package/plugins/core/lib/anyapi/schema-utils.js +25 -16
- package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +0 -64
- package/plugins/core/lib/dbIntrospection.js +228 -21
- package/plugins/core/lib/dbTablesOperations.js +409 -92
- package/plugins/core/lib/fractional-positioning.js +12 -48
- package/plugins/core/lib/querying/identifier-query.js +19 -0
- package/plugins/core/lib/querying/include-query-helpers.js +364 -0
- package/plugins/core/lib/querying/include-to-many.js +428 -0
- package/plugins/core/lib/querying/include-to-one.js +579 -0
- package/plugins/core/lib/querying/knex-cross-table-search.js +31 -307
- package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +99 -401
- package/plugins/core/lib/querying/knex-pagination-helpers.js +67 -330
- package/plugins/core/lib/querying/knex-process-includes.js +5 -110
- package/plugins/core/lib/querying/knex-query-helpers-base.js +21 -56
- package/plugins/core/lib/querying/knex-query-helpers.js +298 -448
- package/plugins/core/lib/querying/knex-relationship-includes.js +120 -2138
- package/plugins/core/lib/querying/knex-window-queries.js +62 -253
- package/plugins/core/lib/querying/query-builder-utils.js +44 -4
- package/plugins/core/lib/querying/query-constraint.d.ts +12 -0
- package/plugins/core/lib/querying/query-constraint.js +27 -0
- package/plugins/core/lib/querying/query-field-sort-helpers.js +205 -30
- package/plugins/core/lib/querying/relationship-identifiers.js +208 -0
- package/plugins/core/lib/querying/sort-helpers.js +20 -1
- package/plugins/core/lib/querying/storage-adapter-utils.js +13 -5
- package/plugins/core/lib/querying/url-helpers.js +11 -4
- package/plugins/core/lib/querying-writing/compile-schemas.js +148 -249
- package/plugins/core/lib/querying-writing/connectors-query-parser.js +33 -118
- package/plugins/core/lib/querying-writing/database-capabilities.js +149 -223
- package/plugins/core/lib/querying-writing/database-value-normalizers.js +238 -187
- package/plugins/core/lib/querying-writing/default-data-helpers.js +15 -45
- package/plugins/core/lib/querying-writing/field-selection-types.d.ts +43 -0
- package/plugins/core/lib/querying-writing/field-utils.js +113 -92
- package/plugins/core/lib/querying-writing/knex-constants.d.ts +18 -0
- package/plugins/core/lib/querying-writing/knex-constants.js +2 -26
- package/plugins/core/lib/querying-writing/knex-field-helpers.js +111 -287
- package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +31 -120
- package/plugins/core/lib/querying-writing/query-field-helpers.js +108 -32
- package/plugins/core/lib/querying-writing/query-field-types.d.ts +60 -0
- package/plugins/core/lib/querying-writing/relationship-contracts.js +33 -13
- package/plugins/core/lib/querying-writing/request-contracts.js +109 -44
- package/plugins/core/lib/querying-writing/resource-id-normalization.js +23 -5
- package/plugins/core/lib/querying-writing/response-options.js +35 -0
- package/plugins/core/lib/querying-writing/schema-helpers.js +203 -324
- package/plugins/core/lib/querying-writing/scope-validations.js +92 -290
- package/plugins/core/lib/querying-writing/simplified-helpers.js +113 -712
- package/plugins/core/lib/storage/canonical-data-helpers.js +683 -0
- package/plugins/core/lib/storage/canonical-storage-mapping.js +51 -20
- package/plugins/core/lib/storage/ordinary-data-helpers.js +710 -0
- package/plugins/core/lib/storage/storage-adapter.js +142 -62
- package/plugins/core/lib/storage/storage-mapping.js +78 -28
- package/plugins/core/lib/storage/storage-types.d.ts +502 -0
- package/plugins/core/lib/writing/many-to-many-manipulations.js +114 -284
- package/plugins/core/lib/writing/relationship-processor.js +94 -158
- package/plugins/core/lib/writing/resource-version.js +172 -0
- package/plugins/core/lib/writing/reverse-relationship-manipulations.js +110 -0
- package/plugins/core/rest-api-anyapi-knex-plugin.js +116 -2509
- package/plugins/core/rest-api-autofilter-plugin.js +17 -19
- package/plugins/core/rest-api-cors-plugin.js +52 -205
- package/plugins/core/rest-api-knex-plugin.js +45 -1069
- package/plugins/core/rest-api-label-plugin.js +25 -38
- package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +5 -5
- package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +9 -6
- package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +7 -26
- package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +20 -26
- package/plugins/core/rest-api-plugin-methods/add-route.js +4 -6
- package/plugins/core/rest-api-plugin-methods/apply-query-filters.js +42 -13
- package/plugins/core/rest-api-plugin-methods/check-permissions.js +11 -5
- package/plugins/core/rest-api-plugin-methods/common.js +291 -385
- package/plugins/core/rest-api-plugin-methods/delete-relationship.js +49 -47
- package/plugins/core/rest-api-plugin-methods/delete.js +31 -25
- package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +108 -145
- package/plugins/core/rest-api-plugin-methods/get-related.js +113 -305
- package/plugins/core/rest-api-plugin-methods/get-relationship.js +25 -18
- package/plugins/core/rest-api-plugin-methods/get.js +38 -69
- package/plugins/core/rest-api-plugin-methods/lifecycle-types.d.ts +225 -0
- package/plugins/core/rest-api-plugin-methods/patch-relationship.js +28 -25
- package/plugins/core/rest-api-plugin-methods/patch.js +46 -99
- package/plugins/core/rest-api-plugin-methods/post-relationship.js +49 -53
- package/plugins/core/rest-api-plugin-methods/post.js +51 -51
- package/plugins/core/rest-api-plugin-methods/put.js +69 -148
- package/plugins/core/rest-api-plugin-methods/query.js +39 -82
- package/plugins/core/rest-api-plugin-methods/release.js +2 -1
- package/plugins/core/rest-api-plugin.js +32 -46
- package/plugins/core/rest-api-positioning-plugin.js +183 -628
- package/plugins/core/rest-api-query-projections-plugin.js +19 -102
- package/plugins/core/rest-api-row-policy-plugin.js +3 -3
- package/plugins/core/socketio-plugin.js +364 -344
- package/plugins/storage/local-storage.js +40 -36
- package/types/autofilter.d.ts +57 -0
- package/types/bulk-methods.d.ts +63 -0
- package/types/errors.d.ts +118 -0
- package/types/fastify-options.d.ts +19 -0
- package/types/file-detectors.d.ts +33 -0
- package/types/file-storage.d.ts +48 -0
- package/types/hook-context.d.ts +180 -0
- package/types/plugin-options.d.ts +53 -0
- package/types/relationship-methods.d.ts +74 -0
- package/types/representations.d.ts +50 -0
- package/types/resource-methods.d.ts +95 -0
- package/types/resource-schema.d.ts +157 -0
- package/types/row-policy.d.ts +31 -0
- package/types/runtime.d.ts +132 -0
- package/types/transactions.d.ts +21 -0
- package/.claude/agents/library-guide-expert.md +0 -30
- package/.claude/agents/rest-api-internals-expert.md +0 -54
- package/.claude/settings.local.json +0 -70
- package/AGENTS.md +0 -34
- package/CLAUDE.md +0 -160
- package/CRUSH.md +0 -55
- package/JSKIT_BACKEND_MIGRATION_TRACKER.txt +0 -180
- package/SINGLE_VALIDATION_CONTRACT_TODO.txt +0 -172
- package/TODO.todo +0 -13
- package/connectors_ideas/fastify.js +0 -217
- package/connectors_ideas/koa-plugin.js +0 -112
- package/docs/.bundle/config +0 -2
- package/docs/COMPARISON.md +0 -433
- package/docs/GUIDE/GUIDE_1_Initial_Setup.md +0 -1057
- package/docs/GUIDE/GUIDE_2_1_The_Starting_Point.md +0 -88
- package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +0 -808
- package/docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md +0 -491
- package/docs/GUIDE/GUIDE_2_4_HasMany_Records.md +0 -398
- package/docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md +0 -898
- package/docs/GUIDE/GUIDE_2_6_Many_To_Many.md +0 -712
- package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +0 -1238
- package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +0 -748
- package/docs/GUIDE/GUIDE_2_9_Relationships_Urls.md +0 -517
- package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +0 -66
- package/docs/GUIDE/GUIDE_3_Field_Transformations.md +0 -1252
- package/docs/GUIDE/GUIDE_5_Non-Db_Resources.md +0 -1107
- package/docs/GUIDE/GUIDE_6_Positioning.md +0 -692
- package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +0 -1491
- package/docs/GUIDE/GUIDE_X_Bulk_Operations.md +0 -732
- package/docs/GUIDE/GUIDE_X_Cors.md +0 -1957
- package/docs/GUIDE/GUIDE_X_Fastify.md +0 -78
- package/docs/GUIDE/GUIDE_X_File_Uploads.md +0 -729
- package/docs/GUIDE/GUIDE_X_Knex_Schema_And_Migrations.md +0 -370
- package/docs/GUIDE/GUIDE_X_Query_Projections.md +0 -159
- package/docs/GUIDE/GUIDE_X_SocketIO.md +0 -1040
- package/docs/GUIDE/GUIDE_X_Writing_Plugins.md +0 -201
- package/docs/Gemfile +0 -6
- package/docs/Gemfile.lock +0 -308
- package/docs/ONBOARDING.md +0 -315
- package/docs/_config.yml +0 -36
- package/docs/_layouts/default.html +0 -123
- package/docs/apple-touch-icon.svg +0 -4
- package/docs/assets/css/style.scss +0 -392
- package/docs/favicon.svg +0 -4
- package/docs/index.md +0 -164
- package/docs/mini-guides/file-uploads-complete.md +0 -161
- package/eslint.config.js +0 -13
- package/examples/url-override-example.js +0 -137
- package/fixing_plan.md +0 -112
- package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +0 -657
- package/plugins/core/lib/querying-writing/return-record-settings.js +0 -47
- package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +0 -110
- package/plugins/storage/s3-storage.js +0 -93
- package/quickTest.js +0 -136
- package/scripts/debug-anyapi-review-filter.mjs +0 -63
- package/scripts/inspect-anyapi-descriptor-country.mjs +0 -19
- package/scripts/inspect-anyapi-descriptor.mjs +0 -19
- package/scripts/run-docs.js +0 -133
- package/test-debug.js +0 -59
- package/tests/README.md +0 -202
- package/tests/TEST_TEMPLATE.test.js +0 -178
- package/tests/anyapi-basic.test.js +0 -105
- package/tests/anyapi-cursor-pagination.test.js +0 -150
- package/tests/anyapi-custom-idproperty.test.js +0 -163
- package/tests/autofilter.test.js +0 -463
- package/tests/bulk-operations.test.js +0 -657
- package/tests/computed-fields.test.js +0 -586
- package/tests/cors.test.js +0 -834
- package/tests/curl.test.js +0 -467
- package/tests/custom-id-mysql-returning-fallback.test.js +0 -128
- package/tests/custom-id-simplified-post.test.js +0 -75
- package/tests/custom-idproperty-relationships.test.js +0 -932
- package/tests/db-introspection.test.js +0 -521
- package/tests/db-table-operations.test.js +0 -798
- package/tests/fastify-plugin.test.js +0 -184
- package/tests/field-getters.test.js +0 -370
- package/tests/field-setters.test.js +0 -306
- package/tests/file-handling.test.js +0 -118
- package/tests/fixtures/api-configs.js +0 -2509
- package/tests/full-jsonapi-belongsto-linkage.test.js +0 -150
- package/tests/helpers/fake-fastify.js +0 -99
- package/tests/helpers/storage-mode.js +0 -65
- package/tests/helpers/test-utils.js +0 -203
- package/tests/http-connectors-parity.test.js +0 -395
- package/tests/id-normalization.test.js +0 -444
- package/tests/include-depth-validation.test.js +0 -331
- package/tests/includes.test.js +0 -427
- package/tests/local-storage.test.js +0 -96
- package/tests/logical-id-write-validation.test.js +0 -151
- package/tests/nested-includes.test.js +0 -617
- package/tests/pagination-cursor-multifield.test.js +0 -643
- package/tests/pagination-enhanced.test.js +0 -563
- package/tests/polymorphic-relationships.test.js +0 -347
- package/tests/polymorphic-simple.test.js +0 -148
- package/tests/positioning-advanced.test.js +0 -692
- package/tests/positioning-extended.test.js +0 -915
- package/tests/positioning-multifilter.test.js +0 -699
- package/tests/positioning.test.js +0 -560
- package/tests/post-commit-hooks.test.js +0 -120
- package/tests/projected-fields.test.js +0 -319
- package/tests/public-exports.test.js +0 -15
- package/tests/put-create-parity.test.js +0 -216
- package/tests/queries.test.js +0 -521
- package/tests/query-limits.test.js +0 -584
- package/tests/relationship-endpoints.test.js +0 -620
- package/tests/relationship-include-storage-adapter.test.js +0 -56
- package/tests/relationships.test.js +0 -681
- package/tests/rest-api.test.js +0 -337
- package/tests/return-record-settings.test.js +0 -184
- package/tests/row-policy.test.js +0 -489
- package/tests/s3-storage.test.js +0 -38
- package/tests/searchschema-knex-behavior.test.js +0 -187
- package/tests/searchschema-merge.test.js +0 -151
- package/tests/socketio.test.js +0 -860
- package/tests/storage-mapping.test.js +0 -362
- package/tests/test-development-guide.md +0 -370
- package/tests/virtual-fields.test.js +0 -404
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
# Complete File Upload Example with Multiple Files
|
|
2
|
-
|
|
3
|
-
This guide shows a complete example of implementing file uploads with jsonrestapi, including:
|
|
4
|
-
- Multiple file fields (images and PDFs)
|
|
5
|
-
- Different storage configurations for different file types
|
|
6
|
-
- HTML form for testing uploads
|
|
7
|
-
- Express static file serving
|
|
8
|
-
|
|
9
|
-
## Complete Example
|
|
10
|
-
|
|
11
|
-
```javascript
|
|
12
|
-
import { Api } from 'hooked-api';
|
|
13
|
-
import { RestApiPlugin, FileHandlingPlugin, ExpressPlugin } from 'json-rest-api';
|
|
14
|
-
import { LocalStorage } from 'json-rest-api/plugins/storage/local-storage.js';
|
|
15
|
-
import express from 'express';
|
|
16
|
-
|
|
17
|
-
// Create API
|
|
18
|
-
const api = new Api({
|
|
19
|
-
name: 'my-library-api',
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
// Create storage for different file types
|
|
23
|
-
const coverStorage = new LocalStorage({
|
|
24
|
-
directory: './uploads/covers',
|
|
25
|
-
baseUrl: 'http://localhost:3000/uploads/covers',
|
|
26
|
-
nameStrategy: 'hash',
|
|
27
|
-
allowedExtensions: ['.jpg', '.jpeg', '.png', '.gif']
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const pdfStorage = new LocalStorage({
|
|
31
|
-
directory: './uploads/pdfs',
|
|
32
|
-
baseUrl: 'http://localhost:3000/uploads/pdfs',
|
|
33
|
-
nameStrategy: 'timestamp'
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
// Use plugins
|
|
37
|
-
api.use(RestApiPlugin);
|
|
38
|
-
api.use(FileHandlingPlugin);
|
|
39
|
-
api.use(ExpressPlugin); // File parser configuration shown in connector plugins section
|
|
40
|
-
|
|
41
|
-
// Define schema with multiple file fields
|
|
42
|
-
api.addResource('books', {
|
|
43
|
-
schema: {
|
|
44
|
-
title: { type: 'string', required: true },
|
|
45
|
-
author: { type: 'string', required: true },
|
|
46
|
-
year: { type: 'number' },
|
|
47
|
-
cover: {
|
|
48
|
-
type: 'file',
|
|
49
|
-
storage: coverStorage,
|
|
50
|
-
accepts: ['image/*'],
|
|
51
|
-
maxSize: '5mb'
|
|
52
|
-
},
|
|
53
|
-
sample: {
|
|
54
|
-
type: 'file',
|
|
55
|
-
storage: pdfStorage,
|
|
56
|
-
accepts: ['application/pdf'],
|
|
57
|
-
maxSize: '10mb',
|
|
58
|
-
required: false
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// Simple data helpers
|
|
64
|
-
api.vars.helpers.dataPost = async ({ scopeName, inputRecord }) => {
|
|
65
|
-
const newBook = {
|
|
66
|
-
id: String(Date.now()),
|
|
67
|
-
...inputRecord.data.attributes
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
console.log('Created book:', newBook);
|
|
71
|
-
|
|
72
|
-
return {
|
|
73
|
-
data: {
|
|
74
|
-
type: 'books',
|
|
75
|
-
id: newBook.id,
|
|
76
|
-
attributes: newBook
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
api.vars.helpers.dataQuery = async () => {
|
|
82
|
-
return { data: [] };
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// Express setup
|
|
86
|
-
const app = express();
|
|
87
|
-
app.use('/uploads', express.static('./uploads'));
|
|
88
|
-
|
|
89
|
-
// Test form
|
|
90
|
-
app.get('/', (req, res) => {
|
|
91
|
-
res.send(`
|
|
92
|
-
<form action="/api/books" method="POST" enctype="multipart/form-data">
|
|
93
|
-
<h2>Add a Book</h2>
|
|
94
|
-
<p>Title: <input name="title" required></p>
|
|
95
|
-
<p>Author: <input name="author" required></p>
|
|
96
|
-
<p>Year: <input name="year" type="number"></p>
|
|
97
|
-
<p>Cover: <input name="cover" type="file" accept="image/*"></p>
|
|
98
|
-
<p>Sample PDF: <input name="sample" type="file" accept=".pdf"></p>
|
|
99
|
-
<button type="submit">Add Book</button>
|
|
100
|
-
</form>
|
|
101
|
-
`);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
api.express.mount(app);
|
|
105
|
-
|
|
106
|
-
app.listen(3000, () => {
|
|
107
|
-
console.log('Library API running at http://localhost:3000');
|
|
108
|
-
console.log('Test form at http://localhost:3000');
|
|
109
|
-
console.log('API endpoints at http://localhost:3000/api/books');
|
|
110
|
-
}).on('error', (err) => {
|
|
111
|
-
console.error('Failed to start server:', err);
|
|
112
|
-
process.exit(1)
|
|
113
|
-
});
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
Remember to install the required peer dependency:
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
npm install busboy
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
## Key Features Demonstrated
|
|
123
|
-
|
|
124
|
-
### Multiple Storage Configurations
|
|
125
|
-
|
|
126
|
-
The example shows how to create different storage configurations for different file types:
|
|
127
|
-
- **Cover images**: Use hash naming strategy and restrict to image files
|
|
128
|
-
- **PDF samples**: Use timestamp naming strategy for PDFs
|
|
129
|
-
|
|
130
|
-
### HTML Test Form
|
|
131
|
-
|
|
132
|
-
The example includes a simple HTML form for testing file uploads without needing external tools.
|
|
133
|
-
|
|
134
|
-
### Static File Serving
|
|
135
|
-
|
|
136
|
-
The Express app serves uploaded files directly:
|
|
137
|
-
```javascript
|
|
138
|
-
app.use('/uploads', express.static('./uploads'));
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
This allows uploaded files to be accessed via URLs like:
|
|
142
|
-
- `http://localhost:3000/uploads/covers/abc123.jpg`
|
|
143
|
-
- `http://localhost:3000/uploads/pdfs/1234567890.pdf`
|
|
144
|
-
|
|
145
|
-
## Testing the Example
|
|
146
|
-
|
|
147
|
-
1. Run the server
|
|
148
|
-
2. Open http://localhost:3000 in your browser
|
|
149
|
-
3. Fill out the form and select files
|
|
150
|
-
4. Submit to see the file upload in action
|
|
151
|
-
5. Check the console for the created book data
|
|
152
|
-
6. Access uploaded files via their URLs
|
|
153
|
-
|
|
154
|
-
## Next Steps
|
|
155
|
-
|
|
156
|
-
- Add validation for file types
|
|
157
|
-
- Implement file deletion when records are deleted
|
|
158
|
-
- Add image resizing for covers
|
|
159
|
-
- Generate thumbnails
|
|
160
|
-
- Add virus scanning for uploaded files
|
|
161
|
-
- Implement S3 storage for production
|
package/eslint.config.js
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Example: Custom URL Override Using Hooks
|
|
5
|
-
*
|
|
6
|
-
* This example demonstrates how to override URL generation using the
|
|
7
|
-
* hooked-api hooks system. This is the recommended approach for custom
|
|
8
|
-
* URL scenarios like API gateways, CDNs, or multi-tenant applications.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { Api } from 'hooked-api'
|
|
12
|
-
import { RestApiPlugin, RestApiKnexPlugin, ExpressPlugin } from 'json-rest-api'
|
|
13
|
-
import knexLib from 'knex'
|
|
14
|
-
import express from 'express'
|
|
15
|
-
|
|
16
|
-
// Create in-memory database
|
|
17
|
-
const knex = knexLib({
|
|
18
|
-
client: 'better-sqlite3',
|
|
19
|
-
connection: { filename: ':memory:' },
|
|
20
|
-
useNullAsDefault: true
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
// Create API with URL override hook
|
|
24
|
-
const api = new Api({
|
|
25
|
-
name: 'url-override-demo',
|
|
26
|
-
|
|
27
|
-
// Define hooks at API creation time
|
|
28
|
-
hooks: {
|
|
29
|
-
// This hook runs at the start of each request
|
|
30
|
-
'transport:request': [
|
|
31
|
-
async (payload) => {
|
|
32
|
-
const { context, req } = payload
|
|
33
|
-
|
|
34
|
-
// Example 1: Override based on custom header
|
|
35
|
-
// Useful for CDN or API gateway scenarios
|
|
36
|
-
if (req?.headers?.['x-public-url']) {
|
|
37
|
-
context.urlPrefixOverride = req.headers['x-public-url']
|
|
38
|
-
console.log(`✓ URL overridden via X-Public-URL header: ${context.urlPrefixOverride}`)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Example 2: API versioning via header
|
|
42
|
-
// Route different API versions while maintaining same backend
|
|
43
|
-
else if (req?.headers?.['x-api-version'] === 'v2') {
|
|
44
|
-
context.urlPrefixOverride = 'https://api.example.com/v2'
|
|
45
|
-
console.log(`✓ URL overridden for API v2: ${context.urlPrefixOverride}`)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Example 3: Multi-tenant based on host
|
|
49
|
-
// Each tenant gets their own domain in responses
|
|
50
|
-
else if (req?.hostname?.includes('tenant-a')) {
|
|
51
|
-
context.urlPrefixOverride = `https://tenant-a.api.com${req.baseUrl || ''}`
|
|
52
|
-
console.log(`✓ URL overridden for tenant-a: ${context.urlPrefixOverride}`)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Example 4: Environment-based override
|
|
56
|
-
// Force production URLs in staging environment
|
|
57
|
-
else if (process.env.FORCE_PRODUCTION_URLS === 'true') {
|
|
58
|
-
context.urlPrefixOverride = 'https://api.production.com/api'
|
|
59
|
-
console.log(`✓ URL overridden via environment: ${context.urlPrefixOverride}`)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return payload
|
|
63
|
-
}
|
|
64
|
-
]
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
// Install plugins
|
|
69
|
-
await api.use(RestApiPlugin, {
|
|
70
|
-
returnRecordTransport: { post: 'full' } // Return full JSON:API response
|
|
71
|
-
})
|
|
72
|
-
await api.use(RestApiKnexPlugin, { knex })
|
|
73
|
-
await api.use(ExpressPlugin, { mountPath: '/api' })
|
|
74
|
-
|
|
75
|
-
// Define a simple resource
|
|
76
|
-
await api.addResource('items', {
|
|
77
|
-
schema: {
|
|
78
|
-
name: { type: 'string', required: true, max: 100 },
|
|
79
|
-
description: { type: 'string', max: 500 }
|
|
80
|
-
}
|
|
81
|
-
})
|
|
82
|
-
await api.resources.items.createKnexTable()
|
|
83
|
-
|
|
84
|
-
// Create some test data
|
|
85
|
-
console.log('\n📝 Creating test data...')
|
|
86
|
-
await api.resources.items.post({
|
|
87
|
-
inputRecord: {
|
|
88
|
-
data: {
|
|
89
|
-
type: 'items',
|
|
90
|
-
attributes: {
|
|
91
|
-
name: 'Test Item 1',
|
|
92
|
-
description: 'This is a test item'
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
simplified: false
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
// Create Express app
|
|
100
|
-
const app = express()
|
|
101
|
-
app.use(api.http.express.router)
|
|
102
|
-
|
|
103
|
-
// Start server
|
|
104
|
-
const PORT = process.env.PORT || 3333
|
|
105
|
-
const server = app.listen(PORT, () => {
|
|
106
|
-
console.log(`\n🚀 Server running on http://localhost:${PORT}`)
|
|
107
|
-
console.log('\n📚 Try these examples:\n')
|
|
108
|
-
|
|
109
|
-
console.log('1. Default URL (auto-detected):')
|
|
110
|
-
console.log(` curl http://localhost:${PORT}/api/items`)
|
|
111
|
-
console.log(' → URLs will be: http://localhost:${PORT}/api/items/1\n')
|
|
112
|
-
|
|
113
|
-
console.log('2. Custom CDN URL:')
|
|
114
|
-
console.log(` curl -H "X-Public-URL: https://cdn.example.com/api" http://localhost:${PORT}/api/items`)
|
|
115
|
-
console.log(' → URLs will be: https://cdn.example.com/api/items/1\n')
|
|
116
|
-
|
|
117
|
-
console.log('3. API Version Header:')
|
|
118
|
-
console.log(` curl -H "X-API-Version: v2" http://localhost:${PORT}/api/items`)
|
|
119
|
-
console.log(' → URLs will be: https://api.example.com/v2/items/1\n')
|
|
120
|
-
|
|
121
|
-
console.log('4. Create with custom URL:')
|
|
122
|
-
console.log(' curl -X POST -H "Content-Type: application/vnd.api+json" \\')
|
|
123
|
-
console.log(' -H "X-Public-URL: https://public.api.com/api" \\')
|
|
124
|
-
console.log(' -d \'{"data":{"type":"items","attributes":{"name":"New Item"}}}\' \\')
|
|
125
|
-
console.log(` http://localhost:${PORT}/api/items`)
|
|
126
|
-
console.log(' → Response URLs will use: https://public.api.com/api/items/2\n')
|
|
127
|
-
|
|
128
|
-
console.log('Press Ctrl+C to stop the server\n')
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
// Graceful shutdown
|
|
132
|
-
process.on('SIGINT', async () => {
|
|
133
|
-
console.log('\n\n👋 Shutting down gracefully...')
|
|
134
|
-
server.close()
|
|
135
|
-
await knex.destroy()
|
|
136
|
-
process.exit(0)
|
|
137
|
-
})
|
package/fixing_plan.md
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
# Fixing Plan
|
|
2
|
-
|
|
3
|
-
Checklist-style plan for addressing the audit findings. Items marked as requiring approval change public behavior, public API, or dependencies.
|
|
4
|
-
|
|
5
|
-
## High Priority
|
|
6
|
-
|
|
7
|
-
- [x] Fix AnyAPI custom `idProperty` canonical mapping.
|
|
8
|
-
- [x] Treat `schemaInfo.idProperty` as an alias of logical `id` in the canonical storage adapter.
|
|
9
|
-
- [x] Skip custom id fields such as `item_id` when building DB attribute selections.
|
|
10
|
-
- [x] Ensure cursor, sort, projection, sparse fieldset, and include paths translate both `id` and `idProperty` to `logical_id`.
|
|
11
|
-
- [x] Add AnyAPI tests for custom-id `post`, `get`, `query`, sparse fields, and cursor pagination.
|
|
12
|
-
|
|
13
|
-
- [x] Fix AnyAPI PUT create parity.
|
|
14
|
-
- [x] Branch `helpers.dataPut` on `context.isCreate`.
|
|
15
|
-
- [x] In create mode, insert into `any_records` with tenant, resource, logical id, and translated attributes.
|
|
16
|
-
- [x] Keep the current update path for update mode.
|
|
17
|
-
- [x] Add legacy Knex and AnyAPI parity tests for `PUT /resource/:id` create, including custom IDs.
|
|
18
|
-
|
|
19
|
-
- [x] Fix pagination link query-string round trips.
|
|
20
|
-
- [x] Replace manual query-string concatenation with one serializer that is the inverse of `parseJsonApiQuery`.
|
|
21
|
-
- [x] Use `URLSearchParams` for keys and values.
|
|
22
|
-
- [x] Map internal `queryParams.filters` back to public JSON:API `filter[...]`.
|
|
23
|
-
- [x] Add tests that parse generated `links.next` and prove filters, fields, sort, include, and page params survive round trip.
|
|
24
|
-
|
|
25
|
-
- [x] Fix relationship PATCH cardinality validation.
|
|
26
|
-
- [x] Generate relationship-aware request contracts from relationship metadata.
|
|
27
|
-
- [x] Allow to-one relationship PATCH data to be `null` or one resource identifier only.
|
|
28
|
-
- [x] Allow to-many relationship data to be arrays only.
|
|
29
|
-
- [x] Make `processRelationships()` throw when a belongsTo relationship receives an array.
|
|
30
|
-
- [x] Add API and transport tests for invalid to-one arrays and invalid to-many scalars.
|
|
31
|
-
|
|
32
|
-
- [x] Run `afterCommit` for relationship routes.
|
|
33
|
-
- [x] After `postRelationship`, `patchRelationship`, and `deleteRelationship` commit their own transaction, run `afterCommit`.
|
|
34
|
-
- [x] Preserve rollback behavior through `afterRollback`.
|
|
35
|
-
- [x] Verify Socket.IO deferred relationship broadcasts work, or define a separate relationship notification policy.
|
|
36
|
-
- [x] Add tests with an `afterCommit` hook counter and Socket.IO relationship-write subscription behavior.
|
|
37
|
-
|
|
38
|
-
- [x] Decide and fix post-commit hook failure semantics.
|
|
39
|
-
- [x] Split transaction work from post-commit side effects.
|
|
40
|
-
- [x] Once `commit()` succeeds, do not call rollback handling.
|
|
41
|
-
- [x] Surface post-commit hook errors while preserving already-committed data.
|
|
42
|
-
- [x] Add tests proving the row remains committed and the response/error semantics are intentional.
|
|
43
|
-
|
|
44
|
-
## Medium Priority
|
|
45
|
-
|
|
46
|
-
- [x] Fix AnyAPI pagination fixture cleanup.
|
|
47
|
-
- [x] Register `cursor_products` and `cursor_items` with `storageMode.registerTable()` in `createCursorPaginationApi`.
|
|
48
|
-
- [x] Add a focused cleanup test proving `cleanTables(knex, ['cursor_products'])` deletes matching `any_records`.
|
|
49
|
-
- [x] Keep metadata-derived cleanup mappings as a later refactor if a broader helper change is approved.
|
|
50
|
-
|
|
51
|
-
- [x] Fix file temp and orphan cleanup.
|
|
52
|
-
- [x] Wrap file processing in `try/finally` so detector temp files are cleaned after validation failures.
|
|
53
|
-
- [x] Track successfully uploaded URLs on `context`.
|
|
54
|
-
- [x] On rollback or later write error, call storage `delete(url)` where available.
|
|
55
|
-
- [x] Document `delete(url)` as the rollback-capable storage contract.
|
|
56
|
-
- [x] Add tests for invalid MIME cleanup and post-upload DB/validation rollback cleanup.
|
|
57
|
-
|
|
58
|
-
- [x] Harden `LocalStorage` path containment.
|
|
59
|
-
- [x] Sanitize or reject custom basenames containing `/`, `\`, drive prefixes, or empty names.
|
|
60
|
-
- [x] Replace `startsWith(resolvedDir)` containment with `path.relative(resolvedDir, resolvedPath)`.
|
|
61
|
-
- [x] Add tests for sibling-prefix traversal such as `../uploads_evil/pwn`.
|
|
62
|
-
|
|
63
|
-
- [x] Fix untrusted proxy URL generation. Requires maintainer approval.
|
|
64
|
-
- [x] Stop trusting `Host` and `X-Forwarded-*` by default for absolute links.
|
|
65
|
-
- [x] Prefer relative links unless `urlPrefixOverride` or an explicit public base URL is configured.
|
|
66
|
-
- [x] If forwarded headers remain supported, gate them behind `trustProxy` and allowed host/proto validation. Resolved by not supporting forwarded headers for URL generation.
|
|
67
|
-
- [x] Add Express and Fastify tests with hostile `Host` and `X-Forwarded-Host` headers.
|
|
68
|
-
|
|
69
|
-
- [x] Fix `S3Storage` non-mock behavior. Dependency/public behavior choice requires maintainer approval.
|
|
70
|
-
- [x] Smallest safe fix: throw a clear "real S3 storage is not implemented" error when `mockMode: false`.
|
|
71
|
-
- [x] Reject full AWS SDK support for this pass because it adds dependency, credential, and public behavior scope.
|
|
72
|
-
- [x] Add tests for mock mode and the chosen non-mock behavior.
|
|
73
|
-
|
|
74
|
-
## Low Priority
|
|
75
|
-
|
|
76
|
-
- [x] Make field setter errors fail closed.
|
|
77
|
-
- [x] Change setter error handling to throw by default.
|
|
78
|
-
- [x] Do not add an opt-in non-fatal mode without a real use case.
|
|
79
|
-
- [x] Add a rollback test where a setter throws and no row is persisted.
|
|
80
|
-
|
|
81
|
-
- [x] Normalize return-record boolean compatibility.
|
|
82
|
-
- [x] Create one shared return-record normalizer.
|
|
83
|
-
- [x] Normalize `true -> 'full'`, `false -> 'no'`, and keep valid strings unchanged.
|
|
84
|
-
- [x] Use the shared normalizer in plugin install and per-call setup.
|
|
85
|
-
- [x] Update tests and docs to prefer `'no'`, `'minimal'`, and `'full'` while preserving boolean compatibility.
|
|
86
|
-
|
|
87
|
-
## Contract Drift
|
|
88
|
-
|
|
89
|
-
- [x] Fix Socket.IO public exports and docs.
|
|
90
|
-
- [x] Export `SocketIOPlugin` from `index.js`.
|
|
91
|
-
- [x] Fix docs to import `Api` from `hooked-api`, not `json-rest-api`.
|
|
92
|
-
- [x] Remove invalid deep imports such as `json-rest-api/plugins/socketio`, or add formal package subpath exports. Subpath exports require maintainer approval.
|
|
93
|
-
- [x] Add a public import smoke test.
|
|
94
|
-
|
|
95
|
-
- [x] Align S3 documentation with implementation.
|
|
96
|
-
- [x] Since real S3 is not implemented, make README and guide text state that the included adapter is mock/demo only.
|
|
97
|
-
- [x] Leave production S3 as a future approved dependency/API design rather than documenting unsupported behavior.
|
|
98
|
-
|
|
99
|
-
- [x] Finish relationship validation contract consolidation.
|
|
100
|
-
- [x] Move relationship-route schemas into the same request-contract surface as resource routes.
|
|
101
|
-
- [x] Do this after the cardinality fix so the centralized contract is correct rather than merely centralized.
|
|
102
|
-
|
|
103
|
-
## Verification Checklist
|
|
104
|
-
|
|
105
|
-
- [x] `npm test`
|
|
106
|
-
- [x] `npm run lint`
|
|
107
|
-
- [x] `npm run test:anyapi`
|
|
108
|
-
- [x] `npm run verify`
|
|
109
|
-
- [x] Narrow repro for AnyAPI custom `idProperty`
|
|
110
|
-
- [x] Narrow repro for relationship route `afterCommit`
|
|
111
|
-
- [x] Narrow repro for pagination link round trip
|
|
112
|
-
- [x] Import smoke test for public exports
|