json-rest-api 1.0.13 → 1.0.15

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 (118) hide show
  1. package/connectors_ideas/fastify.js +66 -68
  2. package/connectors_ideas/koa-plugin.js +50 -51
  3. package/eslint.config.js +13 -0
  4. package/examples/url-override-example.js +63 -63
  5. package/index.js +13 -13
  6. package/lib/enhanced-logger.js +51 -51
  7. package/lib/error-formatter.js +61 -61
  8. package/lib/rest-api-errors.js +28 -28
  9. package/package.json +9 -5
  10. package/plugins/core/bulk-operations-plugin.js +142 -145
  11. package/plugins/core/connectors/express-plugin.js +210 -217
  12. package/plugins/core/connectors/lib/busboy-detector.js +84 -84
  13. package/plugins/core/connectors/lib/formidable-detector.js +50 -50
  14. package/plugins/core/connectors/lib/request-helpers.js +74 -74
  15. package/plugins/core/file-handling-plugin.js +125 -125
  16. package/plugins/core/lib/anyapi/anyapi-registry.js +254 -254
  17. package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +287 -287
  18. package/plugins/core/lib/anyapi/schema-utils.js +101 -101
  19. package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +41 -41
  20. package/plugins/core/lib/dbTablesOperations.js +134 -135
  21. package/plugins/core/lib/fractional-positioning.js +74 -76
  22. package/plugins/core/lib/querying/knex-cross-table-search.js +320 -320
  23. package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +144 -144
  24. package/plugins/core/lib/querying/knex-pagination-helpers.js +164 -164
  25. package/plugins/core/lib/querying/knex-process-includes.js +50 -50
  26. package/plugins/core/lib/querying/knex-query-helpers-base.js +31 -31
  27. package/plugins/core/lib/querying/knex-query-helpers.js +451 -452
  28. package/plugins/core/lib/querying/knex-relationship-includes.js +1160 -1170
  29. package/plugins/core/lib/querying/knex-window-queries.js +89 -89
  30. package/plugins/core/lib/querying/url-helpers.js +35 -35
  31. package/plugins/core/lib/querying-writing/compile-schemas.js +97 -98
  32. package/plugins/core/lib/querying-writing/connectors-query-parser.js +31 -31
  33. package/plugins/core/lib/querying-writing/database-capabilities.js +94 -94
  34. package/plugins/core/lib/querying-writing/database-value-normalizers.js +76 -76
  35. package/plugins/core/lib/querying-writing/default-data-helpers.js +27 -27
  36. package/plugins/core/lib/querying-writing/field-utils.js +44 -42
  37. package/plugins/core/lib/querying-writing/knex-constants.js +15 -16
  38. package/plugins/core/lib/querying-writing/knex-field-helpers.js +117 -115
  39. package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +41 -41
  40. package/plugins/core/lib/querying-writing/payload-validators.js +275 -275
  41. package/plugins/core/lib/querying-writing/schema-helpers.js +129 -129
  42. package/plugins/core/lib/querying-writing/scope-validations.js +143 -146
  43. package/plugins/core/lib/querying-writing/simplified-helpers.js +133 -129
  44. package/plugins/core/lib/storage/storage-adapter.js +68 -68
  45. package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +31 -31
  46. package/plugins/core/lib/writing/many-to-many-manipulations.js +81 -81
  47. package/plugins/core/lib/writing/relationship-processor.js +58 -58
  48. package/plugins/core/rest-api-access.js +8 -8
  49. package/plugins/core/rest-api-anyapi-knex-plugin.js +920 -920
  50. package/plugins/core/rest-api-cors-plugin.js +170 -171
  51. package/plugins/core/rest-api-knex-plugin.js +482 -493
  52. package/plugins/core/rest-api-multihome-plugin.js +247 -251
  53. package/plugins/core/rest-api-plugin-hooks/compile-resource-schemas.js +5 -5
  54. package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +20 -21
  55. package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +44 -44
  56. package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +23 -23
  57. package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +15 -15
  58. package/plugins/core/rest-api-plugin-methods/add-route.js +9 -9
  59. package/plugins/core/rest-api-plugin-methods/check-permissions.js +5 -6
  60. package/plugins/core/rest-api-plugin-methods/common.js +295 -297
  61. package/plugins/core/rest-api-plugin-methods/delete-relationship.js +35 -37
  62. package/plugins/core/rest-api-plugin-methods/delete.js +57 -59
  63. package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +172 -171
  64. package/plugins/core/rest-api-plugin-methods/get-related.js +96 -96
  65. package/plugins/core/rest-api-plugin-methods/get-relationship.js +16 -18
  66. package/plugins/core/rest-api-plugin-methods/get.js +86 -86
  67. package/plugins/core/rest-api-plugin-methods/patch-relationship.js +16 -17
  68. package/plugins/core/rest-api-plugin-methods/patch.js +63 -64
  69. package/plugins/core/rest-api-plugin-methods/post-relationship.js +54 -55
  70. package/plugins/core/rest-api-plugin-methods/post.js +61 -62
  71. package/plugins/core/rest-api-plugin-methods/put.js +86 -87
  72. package/plugins/core/rest-api-plugin-methods/query.js +88 -88
  73. package/plugins/core/rest-api-plugin-methods/release.js +5 -6
  74. package/plugins/core/rest-api-plugin.js +76 -78
  75. package/plugins/core/rest-api-positioning-plugin.js +251 -252
  76. package/plugins/core/socketio-plugin.js +240 -236
  77. package/plugins/storage/local-storage.js +103 -103
  78. package/plugins/storage/s3-storage.js +41 -42
  79. package/quickTest.js +37 -37
  80. package/scripts/debug-anyapi-review-filter.mjs +18 -18
  81. package/scripts/inspect-anyapi-descriptor-country.mjs +10 -10
  82. package/scripts/inspect-anyapi-descriptor.mjs +10 -10
  83. package/scripts/run-docs.js +112 -113
  84. package/test-debug.js +28 -28
  85. package/tests/TEST_TEMPLATE.test.js +51 -51
  86. package/tests/anyapi-basic.test.js +42 -42
  87. package/tests/anyapi-cursor-pagination.test.js +48 -48
  88. package/tests/bulk-operations.test.js +208 -208
  89. package/tests/computed-fields.test.js +204 -204
  90. package/tests/cors.test.js +412 -413
  91. package/tests/curl.test.js +275 -276
  92. package/tests/custom-idproperty-relationships.test.js +266 -266
  93. package/tests/field-getters.test.js +132 -132
  94. package/tests/field-setters.test.js +110 -110
  95. package/tests/fixtures/api-configs.js +699 -699
  96. package/tests/helpers/storage-mode.js +32 -32
  97. package/tests/helpers/test-utils.js +76 -76
  98. package/tests/include-depth-validation.test.js +76 -76
  99. package/tests/includes.test.js +145 -145
  100. package/tests/multihome.test.js +175 -175
  101. package/tests/nested-includes.test.js +255 -255
  102. package/tests/pagination-cursor-multifield.test.js +164 -164
  103. package/tests/pagination-enhanced.test.js +225 -225
  104. package/tests/polymorphic-relationships.test.js +96 -96
  105. package/tests/polymorphic-simple.test.js +44 -44
  106. package/tests/positioning-advanced.test.js +226 -226
  107. package/tests/positioning-extended.test.js +399 -399
  108. package/tests/positioning-multifilter.test.js +204 -204
  109. package/tests/positioning.test.js +266 -266
  110. package/tests/queries.test.js +184 -184
  111. package/tests/query-limits.test.js +154 -155
  112. package/tests/relationship-endpoints.test.js +127 -127
  113. package/tests/relationships.test.js +178 -179
  114. package/tests/rest-api-access.test.js +86 -86
  115. package/tests/rest-api.test.js +103 -103
  116. package/tests/searchschema-merge.test.js +93 -93
  117. package/tests/socketio.test.js +326 -326
  118. package/tests/virtual-fields.test.js +164 -164
@@ -5,8 +5,8 @@
5
5
  *
6
6
  * You'll need to install dependencies: npm install fastify
7
7
  */
8
- import { HookedApiError } from './hooked-api.js';
9
- import { handleFastifyApiError } from './error-handler-fastify.js';
8
+ import { HookedApiError } from './hooked-api.js'
9
+ import { handleFastifyApiError } from './error-handler-fastify.js'
10
10
 
11
11
  /**
12
12
  * **UPGRADED:** Automatically generates a comprehensive Fastify schema object that conforms
@@ -15,19 +15,19 @@ import { handleFastifyApiError } from './error-handler-fastify.js';
15
15
  * @param {object} scopeOptions - The full options object for the scope.
16
16
  * @returns {object} A complete Fastify schema for all CRUD routes.
17
17
  */
18
- function _generateFastifySchema(scopeName, scopeOptions = {}) {
19
- const appSchema = scopeOptions.schema || {};
20
- const relationshipsSchema = scopeOptions.relationships || {};
18
+ function _generateFastifySchema (scopeName, scopeOptions = {}) {
19
+ const appSchema = scopeOptions.schema || {}
20
+ const relationshipsSchema = scopeOptions.relationships || {}
21
21
 
22
22
  // --- Reusable Schema Components ---
23
- const metaObject = { type: 'object', additionalProperties: true };
23
+ const metaObject = { type: 'object', additionalProperties: true }
24
24
  const linksObject = {
25
25
  type: 'object',
26
26
  properties: {
27
27
  self: { type: 'string', format: 'uri-reference' },
28
28
  related: { type: 'string', format: 'uri-reference' },
29
29
  }
30
- };
30
+ }
31
31
  const resourceIdentifierObject = {
32
32
  type: 'object',
33
33
  required: ['type', 'id'],
@@ -35,34 +35,34 @@ function _generateFastifySchema(scopeName, scopeOptions = {}) {
35
35
  type: { type: 'string' },
36
36
  id: { type: 'string' }
37
37
  }
38
- };
38
+ }
39
39
 
40
40
  // --- Build Attribute Properties from simple schema ---
41
- const attributeProperties = {};
42
- const requiredAttributes = [];
41
+ const attributeProperties = {}
42
+ const requiredAttributes = []
43
43
  for (const [key, value] of Object.entries(appSchema)) {
44
- let jsonSchemaType = 'string';
45
- if (value.type === 'number') jsonSchemaType = 'number';
46
- if (value.type === 'boolean') jsonSchemaType = 'boolean';
47
-
48
- attributeProperties[key] = { type: jsonSchemaType };
49
- if (value.format) attributeProperties[key].format = value.format;
50
- if (value.required) requiredAttributes.push(key);
44
+ let jsonSchemaType = 'string'
45
+ if (value.type === 'number') jsonSchemaType = 'number'
46
+ if (value.type === 'boolean') jsonSchemaType = 'boolean'
47
+
48
+ attributeProperties[key] = { type: jsonSchemaType }
49
+ if (value.format) attributeProperties[key].format = value.format
50
+ if (value.required) requiredAttributes.push(key)
51
51
  }
52
-
52
+
53
53
  // --- Build Relationship Properties ---
54
- const relationshipProperties = {};
54
+ const relationshipProperties = {}
55
55
  for (const [key, value] of Object.entries(relationshipsSchema)) {
56
- const isToMany = value.type === 'hasMany';
56
+ const isToMany = value.type === 'hasMany'
57
57
  relationshipProperties[key] = {
58
58
  type: 'object',
59
59
  properties: {
60
60
  links: linksObject,
61
- data: isToMany
62
- ? { type: 'array', items: resourceIdentifierObject }
61
+ data: isToMany
62
+ ? { type: 'array', items: resourceIdentifierObject }
63
63
  : resourceIdentifierObject
64
64
  }
65
- };
65
+ }
66
66
  }
67
67
 
68
68
  // --- Define the core Resource Object schema ---
@@ -82,7 +82,7 @@ function _generateFastifySchema(scopeName, scopeOptions = {}) {
82
82
  },
83
83
  links: linksObject
84
84
  }
85
- };
85
+ }
86
86
 
87
87
  // --- Define the full top-level JSON:API Document ---
88
88
  const singleDocument = {
@@ -93,7 +93,7 @@ function _generateFastifySchema(scopeName, scopeOptions = {}) {
93
93
  links: linksObject,
94
94
  meta: metaObject
95
95
  }
96
- };
96
+ }
97
97
  const collectionDocument = {
98
98
  type: 'object',
99
99
  properties: {
@@ -102,8 +102,7 @@ function _generateFastifySchema(scopeName, scopeOptions = {}) {
102
102
  links: { ...linksObject, properties: { ...linksObject.properties, next: { type: 'string' }, prev: { type: 'string' } } },
103
103
  meta: metaObject
104
104
  }
105
- };
106
-
105
+ }
107
106
 
108
107
  // --- Build the final schema for each route ---
109
108
  return {
@@ -143,77 +142,76 @@ function _generateFastifySchema(scopeName, scopeOptions = {}) {
143
142
  response: { 200: singleDocument }
144
143
  },
145
144
  put: {
146
- body: { /* Similar to POST body */ },
147
- response: { 200: singleDocument }
145
+ body: { /* Similar to POST body */ },
146
+ response: { 200: singleDocument }
148
147
  }
149
- };
148
+ }
150
149
  }
151
150
 
152
-
153
151
  export const HttpFastifyPlugin = {
154
152
  name: 'http-fastify',
155
153
 
156
- install(context, api) {
157
- const { pluginOptions, log } = context;
158
- const httpOptions = pluginOptions.http || {};
159
- const { app, prefix = '/api' } = httpOptions;
154
+ install (context, api) {
155
+ const { pluginOptions, log } = context
156
+ const httpOptions = pluginOptions.http || {}
157
+ const { app, prefix = '/api' } = httpOptions
160
158
 
161
159
  if (!app) {
162
160
  throw new HookedApiError(
163
161
  "The 'http-fastify' plugin requires a Fastify 'app' instance to be passed in the options. Example: api.use(HttpFastifyPlugin, { http: { app: myFastifyApp } })",
164
162
  'CONFIGURATION_ERROR'
165
- );
163
+ )
166
164
  }
167
165
 
168
- log.info(`Fastify HTTP plugin installed. Routes will be prefixed with '${prefix}'.`);
166
+ log.info(`Fastify HTTP plugin installed. Routes will be prefixed with '${prefix}'.`)
169
167
 
170
168
  const _createFastifyHandler = (methodName) => {
171
169
  return async (request, reply) => {
172
- const { scopeName, id } = request.params;
170
+ const { scopeName, id } = request.params
173
171
  const apiParams = {
174
172
  id,
175
173
  inputRecord: request.body,
176
174
  queryParams: request.query,
177
- };
178
- log.debug(`Fastify request for ${scopeName}.${methodName}`, { id, query: apiParams.queryParams });
179
- const result = await api.scopes[scopeName][methodName](apiParams);
175
+ }
176
+ log.debug(`Fastify request for ${scopeName}.${methodName}`, { id, query: apiParams.queryParams })
177
+ const result = await api.scopes[scopeName][methodName](apiParams)
180
178
  switch (methodName) {
181
179
  case 'post':
182
- reply.code(201).send(result);
183
- break;
180
+ reply.code(201).send(result)
181
+ break
184
182
  case 'delete':
185
- reply.code(204).send();
186
- break;
183
+ reply.code(204).send()
184
+ break
187
185
  default:
188
- reply.code(200).send(result);
186
+ reply.code(200).send(result)
189
187
  }
190
- };
191
- };
188
+ }
189
+ }
192
190
 
193
191
  const _createScopePlugin = (scopeName, scopeOptions) => {
194
- const fastifySchema = scopeOptions.fastifySchema || _generateFastifySchema(scopeName, scopeOptions);
192
+ const fastifySchema = scopeOptions.fastifySchema || _generateFastifySchema(scopeName, scopeOptions)
195
193
 
196
194
  return async (fastifyInstance) => {
197
- log.info(`Creating Fastify HTTP routes for scope: '${scopeName}'`);
198
- fastifyInstance.get(`/${scopeName}`, { schema: fastifySchema.query }, _createFastifyHandler('query'));
199
- fastifyInstance.get(`/${scopeName}/:id`, { schema: fastifySchema.get }, _createFastifyHandler('get'));
200
- fastifyInstance.post(`/${scopeName}`, { schema: fastifySchema.post }, _createFastifyHandler('post'));
201
- fastifyInstance.put(`/${scopeName}/:id`, { schema: fastifySchema.put }, _createFastifyHandler('put'));
202
- fastifyInstance.patch(`/${scopeName}/:id`, { schema: fastifySchema.patch }, _createFastifyHandler('patch'));
203
- fastifyInstance.delete(`/${scopeName}/:id`, { schema: fastifySchema.delete }, _createFastifyHandler('delete'));
204
- };
205
- };
206
-
207
- const originalAddScope = api.addScope;
195
+ log.info(`Creating Fastify HTTP routes for scope: '${scopeName}'`)
196
+ fastifyInstance.get(`/${scopeName}`, { schema: fastifySchema.query }, _createFastifyHandler('query'))
197
+ fastifyInstance.get(`/${scopeName}/:id`, { schema: fastifySchema.get }, _createFastifyHandler('get'))
198
+ fastifyInstance.post(`/${scopeName}`, { schema: fastifySchema.post }, _createFastifyHandler('post'))
199
+ fastifyInstance.put(`/${scopeName}/:id`, { schema: fastifySchema.put }, _createFastifyHandler('put'))
200
+ fastifyInstance.patch(`/${scopeName}/:id`, { schema: fastifySchema.patch }, _createFastifyHandler('patch'))
201
+ fastifyInstance.delete(`/${scopeName}/:id`, { schema: fastifySchema.delete }, _createFastifyHandler('delete'))
202
+ }
203
+ }
204
+
205
+ const originalAddScope = api.addScope
208
206
 
209
207
  api.addScope = (name, options, extras) => {
210
- const result = originalAddScope.call(api, name, options, extras);
211
- app.register(_createScopePlugin(name, options), { prefix });
212
- return result;
213
- };
214
-
208
+ const result = originalAddScope.call(api, name, options, extras)
209
+ app.register(_createScopePlugin(name, options), { prefix })
210
+ return result
211
+ }
212
+
215
213
  app.setErrorHandler((error, request, reply) => {
216
- handleFastifyApiError(error, request, reply, log);
217
- });
214
+ handleFastifyApiError(error, request, reply, log)
215
+ })
218
216
  }
219
- };
217
+ }
@@ -6,28 +6,28 @@
6
6
  * It assumes you have `koa-bodyparser` installed and used before this plugin.
7
7
  * You'll need to install dependencies: npm install koa koa-router koa-bodyparser qs
8
8
  */
9
- import KoaRouter from 'koa-router';
10
- import qs from 'qs'; // The same query string parser Express uses
11
- import { HookedApiError } from './hooked-api.js';
12
- import { handleKoaApiError } from './error-handler-koa.js';
9
+ import KoaRouter from 'koa-router'
10
+ import qs from 'qs' // The same query string parser Express uses
11
+ import { HookedApiError } from './hooked-api.js'
12
+ import { handleKoaApiError } from './error-handler-koa.js'
13
13
 
14
14
  export const HttpKoaPlugin = {
15
15
  name: 'http-koa',
16
16
 
17
- install(context, api) {
18
- const { pluginOptions, log } = context;
19
- const httpOptions = pluginOptions.http || {};
20
- const { app, prefix = '/api' } = httpOptions;
17
+ install (context, api) {
18
+ const { pluginOptions, log } = context
19
+ const httpOptions = pluginOptions.http || {}
20
+ const { app, prefix = '/api' } = httpOptions
21
21
 
22
22
  if (!app) {
23
23
  throw new HookedApiError(
24
24
  "The 'http-koa' plugin requires a Koa 'app' instance to be passed in the options. Example: api.use(HttpKoaPlugin, { http: { app: myKoaApp } })",
25
25
  'CONFIGURATION_ERROR'
26
- );
26
+ )
27
27
  }
28
-
28
+
29
29
  // Create a router instance for our API routes
30
- const router = new KoaRouter({ prefix });
30
+ const router = new KoaRouter({ prefix })
31
31
 
32
32
  /**
33
33
  * Creates a generic Koa middleware for a specific API method.
@@ -36,78 +36,77 @@ export const HttpKoaPlugin = {
36
36
  */
37
37
  const _createKoaHandler = (methodName) => {
38
38
  return async (ctx) => {
39
- const { scopeName, id } = ctx.params;
40
-
39
+ const { scopeName, id } = ctx.params
40
+
41
41
  try {
42
42
  // 1. Translate Koa ctx into API method params
43
- const apiParams = {};
44
-
43
+ const apiParams = {}
44
+
45
45
  if (['get', 'put', 'patch', 'delete'].includes(methodName)) {
46
- apiParams.id = id;
46
+ apiParams.id = id
47
47
  }
48
48
  if (['post', 'put', 'patch'].includes(methodName)) {
49
49
  // Assumes koa-bodyparser is used
50
- apiParams.inputRecord = ctx.request.body;
50
+ apiParams.inputRecord = ctx.request.body
51
51
  }
52
52
  // Use qs to properly parse nested query objects
53
- apiParams.queryParams = ctx.querystring ? qs.parse(ctx.querystring) : {};
53
+ apiParams.queryParams = ctx.querystring ? qs.parse(ctx.querystring) : {}
54
54
 
55
- log.debug(`Koa request for ${scopeName}.${methodName}`, { id, query: apiParams.queryParams });
55
+ log.debug(`Koa request for ${scopeName}.${methodName}`, { id, query: apiParams.queryParams })
56
56
 
57
57
  // 2. Execute the corresponding API method
58
- const result = await api.scopes[scopeName][methodName](apiParams);
59
-
58
+ const result = await api.scopes[scopeName][methodName](apiParams)
59
+
60
60
  // 3. Set the successful Koa response
61
61
  switch (methodName) {
62
62
  case 'post':
63
- ctx.status = 201;
64
- ctx.body = result;
65
- break;
63
+ ctx.status = 201
64
+ ctx.body = result
65
+ break
66
66
  case 'delete':
67
- ctx.status = 204;
67
+ ctx.status = 204
68
68
  // No body for 204
69
- break;
69
+ break
70
70
  default:
71
- ctx.status = 200;
72
- ctx.body = result;
71
+ ctx.status = 200
72
+ ctx.body = result
73
73
  }
74
-
75
74
  } catch (error) {
76
75
  // 4. Handle any errors thrown by the API
77
- handleKoaApiError(error, ctx, log);
76
+ handleKoaApiError(error, ctx, log)
78
77
  }
79
- };
80
- };
78
+ }
79
+ }
81
80
 
82
81
  /**
83
82
  * Creates all the standard RESTful routes for a given scope on the router.
84
83
  * @param {string} scopeName - The name of the scope to create routes for.
85
84
  */
86
85
  const _createRoutesForScope = (scopeName) => {
87
- const collectionPath = `/${scopeName}`;
88
- const resourcePath = `${collectionPath}/:id`;
86
+ const collectionPath = `/${scopeName}`
87
+ const resourcePath = `${collectionPath}/:id`
89
88
 
90
- log.info(`Creating Koa HTTP routes for scope: '${scopeName}'`);
89
+ log.info(`Creating Koa HTTP routes for scope: '${scopeName}'`)
91
90
 
92
- router.get(collectionPath, _createKoaHandler('query'));
93
- router.get(resourcePath, _createKoaHandler('get'));
94
- router.post(collectionPath, _createKoaHandler('post'));
95
- router.put(resourcePath, _createKoaHandler('put'));
96
- router.patch(resourcePath, _createKoaHandler('patch'));
97
- router.del(resourcePath, _createKoaHandler('delete')); // .del is koa-router's alias for delete
98
- };
91
+ router.get(collectionPath, _createKoaHandler('query'))
92
+ router.get(resourcePath, _createKoaHandler('get'))
93
+ router.post(collectionPath, _createKoaHandler('post'))
94
+ router.put(resourcePath, _createKoaHandler('put'))
95
+ router.patch(resourcePath, _createKoaHandler('patch'))
96
+ router.del(resourcePath, _createKoaHandler('delete')) // .del is koa-router's alias for delete
97
+ }
99
98
 
100
99
  // --- Main Logic: Wrap `addScope` to automate route creation ---
101
- const originalAddScope = api.addScope;
100
+ const originalAddScope = api.addScope
102
101
 
103
102
  api.addScope = (name, options, extras) => {
104
- const result = originalAddScope.call(api, name, options, extras);
105
- _createRoutesForScope(name);
106
- return result;
107
- };
108
-
103
+ const result = originalAddScope.call(api, name, options, extras)
104
+ _createRoutesForScope(name)
105
+ return result
106
+ }
107
+
109
108
  // Apply the router middleware to the Koa app
110
- app.use(router.routes()).use(router.allowedMethods());
111
- log.info(`Koa router installed with prefix '${prefix}'.`);
109
+ app.use(router.routes()).use(router.allowedMethods())
110
+ log.info(`Koa router installed with prefix '${prefix}'.`)
112
111
  }
113
- };
112
+ }
@@ -0,0 +1,13 @@
1
+ import neostandard from 'neostandard'
2
+
3
+ export default [
4
+ ...neostandard({
5
+ ignores: ['node_modules/**', 'docs/**', 'tests/**', 'examples/**']
6
+ }),
7
+ {
8
+ files: ['index.js'],
9
+ rules: {
10
+ 'import-x/export': 'off'
11
+ }
12
+ }
13
+ ]
@@ -2,75 +2,75 @@
2
2
 
3
3
  /**
4
4
  * Example: Custom URL Override Using Hooks
5
- *
5
+ *
6
6
  * This example demonstrates how to override URL generation using the
7
7
  * hooked-api hooks system. This is the recommended approach for custom
8
8
  * URL scenarios like API gateways, CDNs, or multi-tenant applications.
9
9
  */
10
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';
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
15
 
16
16
  // Create in-memory database
17
17
  const knex = knexLib({
18
18
  client: 'better-sqlite3',
19
19
  connection: { filename: ':memory:' },
20
20
  useNullAsDefault: true
21
- });
21
+ })
22
22
 
23
23
  // Create API with URL override hook
24
- const api = new Api({
24
+ const api = new Api({
25
25
  name: 'url-override-demo',
26
-
26
+
27
27
  // Define hooks at API creation time
28
28
  hooks: {
29
29
  // This hook runs at the start of each request
30
30
  'transport:request': [
31
31
  async (payload) => {
32
- const { context, req } = payload;
33
-
32
+ const { context, req } = payload
33
+
34
34
  // Example 1: Override based on custom header
35
35
  // Useful for CDN or API gateway scenarios
36
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}`);
37
+ context.urlPrefixOverride = req.headers['x-public-url']
38
+ console.log(`āœ“ URL overridden via X-Public-URL header: ${context.urlPrefixOverride}`)
39
39
  }
40
-
40
+
41
41
  // Example 2: API versioning via header
42
42
  // Route different API versions while maintaining same backend
43
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}`);
44
+ context.urlPrefixOverride = 'https://api.example.com/v2'
45
+ console.log(`āœ“ URL overridden for API v2: ${context.urlPrefixOverride}`)
46
46
  }
47
-
47
+
48
48
  // Example 3: Multi-tenant based on host
49
49
  // Each tenant gets their own domain in responses
50
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}`);
51
+ context.urlPrefixOverride = `https://tenant-a.api.com${req.baseUrl || ''}`
52
+ console.log(`āœ“ URL overridden for tenant-a: ${context.urlPrefixOverride}`)
53
53
  }
54
-
54
+
55
55
  // Example 4: Environment-based override
56
56
  // Force production URLs in staging environment
57
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}`);
58
+ context.urlPrefixOverride = 'https://api.production.com/api'
59
+ console.log(`āœ“ URL overridden via environment: ${context.urlPrefixOverride}`)
60
60
  }
61
-
62
- return payload;
61
+
62
+ return payload
63
63
  }
64
64
  ]
65
65
  }
66
- });
66
+ })
67
67
 
68
68
  // Install plugins
69
69
  await api.use(RestApiPlugin, {
70
70
  returnRecordTransport: { post: 'full' } // Return full JSON:API response
71
- });
72
- await api.use(RestApiKnexPlugin, { knex });
73
- await api.use(ExpressPlugin, { mountPath: '/api' });
71
+ })
72
+ await api.use(RestApiKnexPlugin, { knex })
73
+ await api.use(ExpressPlugin, { mountPath: '/api' })
74
74
 
75
75
  // Define a simple resource
76
76
  await api.addResource('items', {
@@ -78,11 +78,11 @@ await api.addResource('items', {
78
78
  name: { type: 'string', required: true, max: 100 },
79
79
  description: { type: 'string', max: 500 }
80
80
  }
81
- });
82
- await api.resources.items.createKnexTable();
81
+ })
82
+ await api.resources.items.createKnexTable()
83
83
 
84
84
  // Create some test data
85
- console.log('\nšŸ“ Creating test data...');
85
+ console.log('\nšŸ“ Creating test data...')
86
86
  await api.resources.items.post({
87
87
  inputRecord: {
88
88
  data: {
@@ -94,44 +94,44 @@ await api.resources.items.post({
94
94
  }
95
95
  },
96
96
  simplified: false
97
- });
97
+ })
98
98
 
99
99
  // Create Express app
100
- const app = express();
101
- app.use(api.http.express.router);
100
+ const app = express()
101
+ app.use(api.http.express.router)
102
102
 
103
103
  // Start server
104
- const PORT = process.env.PORT || 3333;
104
+ const PORT = process.env.PORT || 3333
105
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
- });
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
130
 
131
131
  // Graceful shutdown
132
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
- });
133
+ console.log('\n\nšŸ‘‹ Shutting down gracefully...')
134
+ server.close()
135
+ await knex.destroy()
136
+ process.exit(0)
137
+ })
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * JSON REST API - REST API plugin for hooked-api with JSON:API compliance
3
- *
3
+ *
4
4
  * This package provides:
5
5
  * - REST API plugin that adds query, get, post, put, patch, delete methods to scopes
6
6
  * - Connector plugins for various HTTP frameworks (Express, etc.)
@@ -9,33 +9,33 @@
9
9
  */
10
10
 
11
11
  // Core plugins
12
- export { RestApiPlugin } from './plugins/core/rest-api-plugin.js';
13
- export { FileHandlingPlugin } from './plugins/core/file-handling-plugin.js';
14
- export { CorsPlugin } from './plugins/core/rest-api-cors-plugin.js';
15
- export { AccessPlugin } from './plugins/core/rest-api-access.js';
12
+ export { RestApiPlugin } from './plugins/core/rest-api-plugin.js'
13
+ export { FileHandlingPlugin } from './plugins/core/file-handling-plugin.js'
14
+ export { CorsPlugin } from './plugins/core/rest-api-cors-plugin.js'
15
+ export { AccessPlugin } from './plugins/core/rest-api-access.js'
16
16
 
17
17
  // Database plugins
18
- export { RestApiKnexPlugin } from './plugins/core/rest-api-knex-plugin.js';
19
- export { RestApiAnyapiKnexPlugin } from './plugins/core/rest-api-anyapi-knex-plugin.js';
18
+ export { RestApiKnexPlugin } from './plugins/core/rest-api-knex-plugin.js'
19
+ export { RestApiAnyapiKnexPlugin } from './plugins/core/rest-api-anyapi-knex-plugin.js'
20
20
 
21
21
  // Connector plugins
22
- export { ExpressPlugin } from './plugins/core/connectors/express-plugin.js';
22
+ export { ExpressPlugin } from './plugins/core/connectors/express-plugin.js'
23
23
  // Future: export { FastifyPlugin } from './plugins/core/connectors/fastify-plugin.js';
24
24
  // Future: export { KoaPlugin } from './plugins/core/connectors/koa-plugin.js';
25
25
 
26
26
  // Storage plugins for file handling
27
- export { LocalStorage } from './plugins/storage/local-storage.js';
28
- export { S3Storage } from './plugins/storage/s3-storage.js';
27
+ export { LocalStorage } from './plugins/storage/local-storage.js'
28
+ export { S3Storage } from './plugins/storage/s3-storage.js'
29
29
 
30
30
  // Error classes for consumers who need them
31
31
  export {
32
32
  RestApiValidationError,
33
33
  RestApiResourceError,
34
34
  RestApiPayloadError
35
- } from './lib/rest-api-errors.js';
35
+ } from './lib/rest-api-errors.js'
36
36
 
37
37
  // Re-export everything for backward compatibility
38
- export * from './lib/rest-api-errors.js';
38
+ export * from './lib/rest-api-errors.js'
39
39
 
40
40
  // URL helper function for generating API URL prefix
41
- export { getUrlPrefix } from './plugins/core/lib/querying/url-helpers.js';
41
+ export { getUrlPrefix } from './plugins/core/lib/querying/url-helpers.js'