json-rest-api 0.1.1 → 1.0.1

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 (50) hide show
  1. package/.claude/settings.local.json +16 -0
  2. package/LICENSE +674 -20
  3. package/README.md +728 -120
  4. package/RELATIONSHIPS.md +1013 -0
  5. package/TODO_rest-api-plugin-extras.js +142 -0
  6. package/connectors_ideas/express-plugin.js +120 -0
  7. package/connectors_ideas/fastify.js +219 -0
  8. package/connectors_ideas/http-plugin.js +138 -0
  9. package/connectors_ideas/koa-plugin.js +113 -0
  10. package/docs/fileUploads.md +722 -0
  11. package/docs/mini-guides/file-uploads-complete.md +159 -0
  12. package/examples/.gitkeep +0 -0
  13. package/examples/express-example.js +262 -0
  14. package/examples/express-plugin.js +366 -0
  15. package/examples/file-upload-example.js +205 -0
  16. package/examples/filename-strategies.js +140 -0
  17. package/examples/knex-basic.js +66 -0
  18. package/index.js +28 -202
  19. package/lib/payload-validators.js +685 -0
  20. package/lib/rest-api-errors.js +58 -0
  21. package/package.json +37 -20
  22. package/payloadValdators.js +686 -0
  23. package/plugins/core/connectors/express-plugin.js +449 -0
  24. package/plugins/core/connectors/http-plugin.js +450 -0
  25. package/plugins/core/file-handling-plugin.js +338 -0
  26. package/plugins/core/lib/busboy-detector.js +167 -0
  27. package/plugins/core/lib/formidable-detector.js +130 -0
  28. package/plugins/core/rest-api-knex-plugin.js +200 -0
  29. package/plugins/core/rest-api-plugin.js +1419 -0
  30. package/plugins/storage/local-storage.js +218 -0
  31. package/plugins/storage/s3-storage.js +125 -0
  32. package/test-examples/package-lock.json +906 -0
  33. package/test-examples/package.json +15 -0
  34. package/test-examples/test-basic-api.js +11 -0
  35. package/test-examples/test-books-resource.js +116 -0
  36. package/test-examples/test-complete-file-upload.js +140 -0
  37. package/test-examples/test-debug.js +28 -0
  38. package/test-examples/test-express-plugin.js +177 -0
  39. package/test-examples/test-express-simple.js +28 -0
  40. package/test-examples/test-express-with-import.js +71 -0
  41. package/test-examples/test-file-upload.js +132 -0
  42. package/test-examples/test-http-plugin.js +159 -0
  43. package/test-examples/test-programmatic-api.js +188 -0
  44. package/test-examples/test-rest-api-plugin.js +13 -0
  45. package/.jshintrc +0 -48
  46. package/.npmignore +0 -15
  47. package/examples/client.js +0 -18
  48. package/examples/client2.js +0 -27
  49. package/examples/server.js +0 -23
  50. package/test.js +0 -101
@@ -0,0 +1,200 @@
1
+
2
+ export const RestApiKnexPlugin = {
3
+ name: 'rest-api-knex',
4
+ dependencies: ['rest-api'],
5
+
6
+ async install({ helpers, vars, pluginOptions, api, log, scopes }) {
7
+
8
+ // Get Knex configuration from plugin options
9
+ const knexOptions = pluginOptions.knex || pluginOptions['rest-api-knex'];
10
+ if (!knexOptions || !knexOptions.knex) {
11
+ throw new Error('RestApiKnexPlugin requires a knex instance in pluginOptions');
12
+ }
13
+
14
+ const knex = knexOptions.knex;
15
+
16
+ // Helper to get table name for a scope
17
+ const getTableName = (scopeName) => {
18
+ const scopeOptions = scopes[scopeName]?.options || {};
19
+ return scopeOptions.tableName || scopeName;
20
+ };
21
+
22
+ // Helper to convert DB record to JSON:API format
23
+ const toJsonApi = (scopeName, record) => {
24
+ if (!record) return null;
25
+
26
+ const idProperty = vars.idProperty || 'id';
27
+ const { [idProperty]: id, ...attributes } = record;
28
+
29
+ return {
30
+ type: scopeName,
31
+ id: String(id),
32
+ attributes
33
+ };
34
+ };
35
+
36
+ // GET - retrieve a single record by ID
37
+ helpers.dataGet = async ({ scopeName, id }) => {
38
+ const tableName = getTableName(scopeName);
39
+ const idProperty = vars.idProperty || 'id';
40
+
41
+ log.debug(`[Knex] GET ${tableName}/${id}`);
42
+
43
+ const record = await knex(tableName)
44
+ .where(idProperty, id)
45
+ .first();
46
+
47
+ if (!record) {
48
+ const error = new Error(`Record not found: ${scopeName}/${id}`);
49
+ error.code = 'REST_API_RESOURCE';
50
+ error.subtype = 'not_found';
51
+ throw error;
52
+ }
53
+
54
+ return {
55
+ data: toJsonApi(scopeName, record)
56
+ };
57
+ };
58
+
59
+ // QUERY - retrieve multiple records
60
+ helpers.dataQuery = async ({ scopeName, queryParams = {} }) => {
61
+ const tableName = getTableName(scopeName);
62
+
63
+ log.debug(`[Knex] QUERY ${tableName}`, queryParams);
64
+
65
+ // Very basic implementation - just get all records
66
+ // Ignoring include, fields, filter, sort, page for now
67
+ const records = await knex(tableName).select('*');
68
+
69
+ return {
70
+ data: records.map(record => toJsonApi(scopeName, record))
71
+ };
72
+ };
73
+
74
+ // POST - create a new record
75
+ helpers.dataPost = async ({ scopeName, inputRecord }) => {
76
+ const tableName = getTableName(scopeName);
77
+ const idProperty = vars.idProperty || 'id';
78
+
79
+ log.debug(`[Knex] POST ${tableName}`, inputRecord);
80
+
81
+ // Extract attributes from JSON:API format
82
+ const attributes = inputRecord.data.attributes;
83
+
84
+ // Insert and get the new ID
85
+ const [id] = await knex(tableName).insert(attributes).returning(idProperty);
86
+
87
+ // Fetch the created record
88
+ const newRecord = await knex(tableName)
89
+ .where(idProperty, id)
90
+ .first();
91
+
92
+ return {
93
+ data: toJsonApi(scopeName, newRecord)
94
+ };
95
+ };
96
+
97
+ // PUT - replace an entire record
98
+ helpers.dataPut = async ({ scopeName, id, inputRecord }) => {
99
+ const tableName = getTableName(scopeName);
100
+ const idProperty = vars.idProperty || 'id';
101
+
102
+ log.debug(`[Knex] PUT ${tableName}/${id}`, inputRecord);
103
+
104
+ // Extract attributes from JSON:API format
105
+ const attributes = inputRecord.data.attributes;
106
+
107
+ // Check if record exists
108
+ const exists = await knex(tableName)
109
+ .where(idProperty, id)
110
+ .first();
111
+
112
+ if (!exists) {
113
+ const error = new Error(`Record not found: ${scopeName}/${id}`);
114
+ error.code = 'REST_API_RESOURCE';
115
+ error.subtype = 'not_found';
116
+ throw error;
117
+ }
118
+
119
+ // Update the record (replace all fields)
120
+ await knex(tableName)
121
+ .where(idProperty, id)
122
+ .update(attributes);
123
+
124
+ // Fetch the updated record
125
+ const updatedRecord = await knex(tableName)
126
+ .where(idProperty, id)
127
+ .first();
128
+
129
+ return {
130
+ data: toJsonApi(scopeName, updatedRecord)
131
+ };
132
+ };
133
+
134
+ // PATCH - partially update a record
135
+ helpers.dataPatch = async ({ scopeName, id, inputRecord }) => {
136
+ const tableName = getTableName(scopeName);
137
+ const idProperty = vars.idProperty || 'id';
138
+
139
+ log.debug(`[Knex] PATCH ${tableName}/${id}`, inputRecord);
140
+
141
+ // Extract attributes from JSON:API format
142
+ const attributes = inputRecord.data.attributes;
143
+
144
+ // Check if record exists
145
+ const exists = await knex(tableName)
146
+ .where(idProperty, id)
147
+ .first();
148
+
149
+ if (!exists) {
150
+ const error = new Error(`Record not found: ${scopeName}/${id}`);
151
+ error.code = 'REST_API_RESOURCE';
152
+ error.subtype = 'not_found';
153
+ throw error;
154
+ }
155
+
156
+ // Update only provided fields
157
+ await knex(tableName)
158
+ .where(idProperty, id)
159
+ .update(attributes);
160
+
161
+ // Fetch the updated record
162
+ const updatedRecord = await knex(tableName)
163
+ .where(idProperty, id)
164
+ .first();
165
+
166
+ return {
167
+ data: toJsonApi(scopeName, updatedRecord)
168
+ };
169
+ };
170
+
171
+ // DELETE - remove a record
172
+ helpers.dataDelete = async ({ scopeName, id }) => {
173
+ const tableName = getTableName(scopeName);
174
+ const idProperty = vars.idProperty || 'id';
175
+
176
+ log.debug(`[Knex] DELETE ${tableName}/${id}`);
177
+
178
+ // Check if record exists
179
+ const exists = await knex(tableName)
180
+ .where(idProperty, id)
181
+ .first();
182
+
183
+ if (!exists) {
184
+ const error = new Error(`Record not found: ${scopeName}/${id}`);
185
+ error.code = 'REST_API_RESOURCE';
186
+ error.subtype = 'not_found';
187
+ throw error;
188
+ }
189
+
190
+ // Delete the record
191
+ await knex(tableName)
192
+ .where(idProperty, id)
193
+ .delete();
194
+
195
+ return { success: true };
196
+ };
197
+
198
+ log.info('RestApiKnexPlugin installed - basic CRUD operations ready');
199
+ }
200
+ }