json-rest-api 1.0.14 → 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 +11 -2
  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 +1 -1
  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
@@ -1,72 +1,72 @@
1
- import { RestApiResourceError } from "../../../lib/rest-api-errors.js";
2
- import { findRelationshipDefinition } from "./common.js";
3
- import { buildRelationshipUrl } from "../lib/querying/url-helpers.js";
1
+ import { RestApiResourceError } from '../../../lib/rest-api-errors.js'
2
+ import { findRelationshipDefinition } from './common.js'
3
+ import { buildRelationshipUrl } from '../lib/querying/url-helpers.js'
4
4
 
5
5
  /**
6
6
  * GET RELATED
7
7
  * Retrieves the actual related resources (full data)
8
8
  * GET /api/articles/1/comments
9
- *
9
+ *
10
10
  * @param {string} id - The ID of the parent resource
11
11
  * @param {string} relationshipName - The name of the relationship
12
12
  * @param {object} queryParams - Standard query parameters
13
13
  * @returns {Promise<object>} Related resources with full data
14
14
  */
15
- export default async function getRelatedMethod ({ params, context, vars, helpers, scope, scopes, runHooks, scopeName, api }) {
16
- context.method = 'getRelated';
17
- context.id = params.id;
18
- context.relationshipName = params.relationshipName;
19
- context.queryParams = params.queryParams || {};
20
- context.schemaInfo = scopes[scopeName].vars.schemaInfo;
21
- context.transaction = params.transaction;
15
+ export default async function getRelatedMethod ({ params, context, vars, helpers, scope, scopes, runHooks, scopeName, api }) {
16
+ context.method = 'getRelated'
17
+ context.id = params.id
18
+ context.relationshipName = params.relationshipName
19
+ context.queryParams = params.queryParams || {}
20
+ context.schemaInfo = scopes[scopeName].vars.schemaInfo
21
+ context.transaction = params.transaction
22
22
  context.db = context.transaction || api.knex.instance
23
23
 
24
24
  // Validate the relationship exists
25
- const relDef = findRelationshipDefinition(context.schemaInfo, context.relationshipName);
25
+ const relDef = findRelationshipDefinition(context.schemaInfo, context.relationshipName)
26
26
 
27
27
  if (!relDef) {
28
28
  throw new RestApiResourceError(
29
29
  `Relationship '${context.relationshipName}' not found on resource '${scopeName}'`,
30
30
  { subtype: 'relationship_not_found' }
31
- );
31
+ )
32
32
  }
33
33
 
34
34
  // Determine target type based on relationship type
35
- let targetType;
35
+ let targetType
36
36
  if (relDef.type === 'hasMany' || relDef.type === 'hasOne') {
37
- targetType = relDef.target;
37
+ targetType = relDef.target
38
38
  } else if (relDef.type === 'manyToMany') {
39
- targetType = context.relationshipName; // For manyToMany, use the relationship name
39
+ targetType = context.relationshipName // For manyToMany, use the relationship name
40
40
  } else if (relDef.belongsTo) {
41
- targetType = relDef.belongsTo; // belongsTo still in schema
41
+ targetType = relDef.belongsTo // belongsTo still in schema
42
42
  }
43
43
 
44
44
  if (!targetType || !scopes[targetType]) {
45
45
  throw new RestApiResourceError(
46
46
  `Related resource type '${targetType}' not found`,
47
47
  { subtype: 'related_type_not_found' }
48
- );
48
+ )
49
49
  }
50
50
 
51
51
  // Check permissions
52
- await runHooks('checkPermissions');
53
- await runHooks('checkPermissionsGetRelated');
52
+ await runHooks('checkPermissions')
53
+ await runHooks('checkPermissionsGetRelated')
54
54
 
55
55
  // Verify parent exists
56
56
  const exists = await helpers.dataExists({
57
57
  scopeName,
58
58
  context: { db: context.db, id: context.id, schemaInfo: context.schemaInfo }
59
- });
59
+ })
60
60
 
61
61
  if (!exists) {
62
- throw new RestApiResourceError('Resource not found', { subtype: 'not_found' });
62
+ throw new RestApiResourceError('Resource not found', { subtype: 'not_found' })
63
63
  }
64
64
 
65
65
  // Handle to-one relationships (belongsTo and hasOne)
66
66
  // For example: GET /api/books/1/country or GET /api/books/1/publisher
67
67
  if (relDef.belongsTo || relDef.type === 'hasOne') {
68
68
  // OPTIMIZATION: Detect if we actually need to make two API calls
69
- //
69
+ //
70
70
  // The naive approach always makes 2 calls:
71
71
  // 1. Get parent with relationship included (fetches FULL related record)
72
72
  // 2. Extract just the ID and fetch the same record again with queryParams
@@ -81,30 +81,30 @@ export default async function getRelatedMethod ({ params, context, vars, helpers
81
81
  // Note: Filters and sorting don't make sense for a single to-one relationship
82
82
  // so we don't check for them
83
83
  false
84
- );
84
+ )
85
85
 
86
86
  if (hasRelevantQueryParams) {
87
87
  // CASE 1: Has queryParams that affect the related resource
88
88
  // We need to make two calls to properly apply the queryParams
89
-
89
+
90
90
  // First call: Get parent with minimal data (just need the related ID)
91
91
  const parent = await scope.get({
92
92
  id: context.id,
93
- queryParams: {
93
+ queryParams: {
94
94
  include: [context.relationshipName],
95
95
  fields: { [scopeName]: vars.idProperty || 'id' } // Only fetch parent ID to minimize data
96
96
  },
97
97
  transaction: context.transaction,
98
98
  simplified: false,
99
99
  isTransport: params.isTransport
100
- });
100
+ })
101
101
 
102
- const relatedId = parent.data.relationships?.[context.relationshipName]?.data?.id;
102
+ const relatedId = parent.data.relationships?.[context.relationshipName]?.data?.id
103
103
  if (!relatedId) {
104
104
  return {
105
105
  links: { self: buildRelationshipUrl(context, scope, scopeName, context.id, context.relationshipName, false) },
106
106
  data: null
107
- };
107
+ }
108
108
  }
109
109
 
110
110
  // Second call: Get the related resource with all queryParams applied
@@ -114,48 +114,48 @@ export default async function getRelatedMethod ({ params, context, vars, helpers
114
114
  transaction: context.transaction,
115
115
  simplified: false,
116
116
  isTransport: params.isTransport
117
- });
117
+ })
118
118
 
119
119
  return {
120
120
  links: { self: buildRelationshipUrl(context, scope, scopeName, context.id, context.relationshipName, false) },
121
121
  data: related.data,
122
122
  included: related.included
123
- };
123
+ }
124
124
  } else {
125
125
  // CASE 2: No queryParams that affect the related resource
126
126
  // We can get everything in one call and extract from included
127
-
127
+
128
128
  // Single call: Get parent with full related resource included
129
129
  const parent = await scope.get({
130
130
  id: context.id,
131
- queryParams: {
131
+ queryParams: {
132
132
  include: [context.relationshipName],
133
133
  fields: context.queryParams.fields // Respect any field selections for the parent
134
134
  },
135
135
  transaction: context.transaction,
136
136
  simplified: false,
137
137
  isTransport: params.isTransport
138
- });
138
+ })
139
139
 
140
140
  // Extract the related resource from the parent's relationships
141
- const relatedId = parent.data.relationships?.[context.relationshipName]?.data?.id;
141
+ const relatedId = parent.data.relationships?.[context.relationshipName]?.data?.id
142
142
  if (!relatedId) {
143
143
  return {
144
144
  links: { self: buildRelationshipUrl(context, scope, scopeName, context.id, context.relationshipName, false) },
145
145
  data: null
146
- };
146
+ }
147
147
  }
148
148
 
149
149
  // Find the full related resource in the included array
150
150
  // The include system already fetched it for us!
151
151
  const relatedResource = parent.included?.find(
152
152
  r => r.type === targetType && r.id === relatedId
153
- );
153
+ )
154
154
 
155
155
  return {
156
156
  links: { self: buildRelationshipUrl(context, scope, scopeName, context.id, context.relationshipName, false) },
157
157
  data: relatedResource || null
158
- };
158
+ }
159
159
  }
160
160
  }
161
161
 
@@ -165,71 +165,71 @@ export default async function getRelatedMethod ({ params, context, vars, helpers
165
165
  if (relDef.via) {
166
166
  // Polymorphic hasMany relationship
167
167
  // Example: publishers hasMany reviews via reviewable
168
- const targetRelationships = scopes[targetType].vars.schemaInfo.schemaRelationships;
169
- const viaRel = targetRelationships?.[relDef.via];
170
-
168
+ const targetRelationships = scopes[targetType].vars.schemaInfo.schemaRelationships
169
+ const viaRel = targetRelationships?.[relDef.via]
170
+
171
171
  if (!viaRel?.belongsToPolymorphic) {
172
172
  throw new RestApiResourceError(
173
173
  `Via relationship '${relDef.via}' not found or not polymorphic in '${targetType}'`,
174
174
  { subtype: 'invalid_via_relationship' }
175
- );
175
+ )
176
176
  }
177
-
178
- const { typeField, idField } = viaRel.belongsToPolymorphic;
179
-
177
+
178
+ const { typeField, idField } = viaRel.belongsToPolymorphic
179
+
180
180
  // Add polymorphic filters
181
181
  const filters = {
182
182
  ...context.queryParams.filters,
183
183
  [typeField]: scopeName,
184
184
  [idField]: context.id
185
- };
186
-
185
+ }
186
+
187
187
  const result = await api.resources[targetType].query({
188
188
  queryParams: { ...context.queryParams, filters },
189
189
  transaction: context.transaction,
190
190
  simplified: false,
191
191
  isTransport: params.isTransport
192
- });
193
-
192
+ })
193
+
194
194
  if (!result.links) {
195
- result.links = {};
195
+ result.links = {}
196
196
  }
197
- result.links.self = buildRelationshipUrl(context, scope, scopeName, context.id, context.relationshipName, false);
198
- return result;
197
+ result.links.self = buildRelationshipUrl(context, scope, scopeName, context.id, context.relationshipName, false)
198
+ return result
199
199
  } else {
200
200
  // Regular hasMany with foreignKey
201
201
  // Need to find the relationship name in the target resource that points back to this resource
202
- const targetSchema = scopes[targetType].vars.schemaInfo.schemaStructure;
203
- let relationshipFilterName = null;
204
-
202
+ const targetSchema = scopes[targetType].vars.schemaInfo.schemaStructure
203
+ let relationshipFilterName = null
204
+
205
205
  // Find the field in target schema that has the foreign key and get its relationship name
206
206
  for (const [fieldName, fieldDef] of Object.entries(targetSchema)) {
207
207
  if (fieldName === relDef.foreignKey && fieldDef.belongsTo === scopeName && fieldDef.as) {
208
- relationshipFilterName = fieldDef.as;
209
- break;
208
+ relationshipFilterName = fieldDef.as
209
+ break
210
210
  }
211
211
  }
212
-
212
+
213
213
  // Fall back to foreign key if no relationship name found (shouldn't happen with proper schema)
214
- const filterKey = relationshipFilterName || relDef.foreignKey;
215
-
214
+ const filterKey = relationshipFilterName || relDef.foreignKey
215
+
216
216
  const filters = {
217
217
  ...context.queryParams.filters,
218
218
  [filterKey]: context.id
219
- };
219
+ }
220
220
 
221
221
  const result = await api.resources[targetType].query({
222
222
  queryParams: { ...context.queryParams, filters },
223
223
  transaction: context.transaction,
224
224
  simplified: false,
225
225
  isTransport: params.isTransport
226
- });
226
+ })
227
227
 
228
228
  if (!result.links) {
229
- result.links = {};
229
+ result.links = {}
230
230
  }
231
- result.links.self = buildRelationshipUrl(context, scope, scopeName, context.id, context.relationshipName, false);
232
- return result;
231
+ result.links.self = buildRelationshipUrl(context, scope, scopeName, context.id, context.relationshipName, false)
232
+ return result
233
233
  }
234
234
  }
235
235
 
@@ -241,9 +241,9 @@ export default async function getRelatedMethod ({ params, context, vars, helpers
241
241
  context,
242
242
  scopeName,
243
243
  relName: context.relationshipName,
244
- });
244
+ })
245
245
 
246
- const results = [];
246
+ const results = []
247
247
  for (const identifier of identifiers) {
248
248
  const related = await api.resources[targetType].get({
249
249
  id: identifier.id,
@@ -251,9 +251,9 @@ export default async function getRelatedMethod ({ params, context, vars, helpers
251
251
  transaction: context.transaction,
252
252
  simplified: false,
253
253
  isTransport: params.isTransport,
254
- });
254
+ })
255
255
  if (related?.data) {
256
- results.push(related.data);
256
+ results.push(related.data)
257
257
  }
258
258
  }
259
259
 
@@ -262,35 +262,35 @@ export default async function getRelatedMethod ({ params, context, vars, helpers
262
262
  self: buildRelationshipUrl(context, scope, scopeName, context.id, context.relationshipName, false)
263
263
  },
264
264
  data: results,
265
- };
265
+ }
266
266
  }
267
267
 
268
268
  // Legacy fallback
269
- const pivotResource = relDef.through;
270
- const foreignKey = relDef.foreignKey;
271
- const otherKey = relDef.otherKey;
269
+ const pivotResource = relDef.through
270
+ const foreignKey = relDef.foreignKey
271
+ const otherKey = relDef.otherKey
272
272
  if (!foreignKey || !otherKey) {
273
- throw new Error(`Missing foreignKey or otherKey in many-to-many relationship`);
273
+ throw new Error('Missing foreignKey or otherKey in many-to-many relationship')
274
274
  }
275
- const pivotScope = scopes[pivotResource];
275
+ const pivotScope = scopes[pivotResource]
276
276
  if (!pivotScope) {
277
277
  throw new RestApiResourceError(
278
278
  `Pivot table resource '${pivotResource}' not found`,
279
279
  { subtype: 'pivot_table_not_found' }
280
- );
280
+ )
281
281
  }
282
- const pivotSchema = pivotScope.vars.schemaInfo.schemaStructure;
283
- let parentRelationshipName = null;
282
+ const pivotSchema = pivotScope.vars.schemaInfo.schemaStructure
283
+ let parentRelationshipName = null
284
284
  for (const [fieldName, fieldDef] of Object.entries(pivotSchema)) {
285
285
  if (fieldName === foreignKey && fieldDef.belongsTo === scopeName && fieldDef.as) {
286
- parentRelationshipName = fieldDef.as;
287
- break;
286
+ parentRelationshipName = fieldDef.as
287
+ break
288
288
  }
289
289
  }
290
- const filterKey = parentRelationshipName || foreignKey;
290
+ const filterKey = parentRelationshipName || foreignKey
291
291
  const pivotFilters = {
292
292
  [filterKey]: context.id
293
- };
293
+ }
294
294
  const pivotResult = await api.resources[pivotResource].query({
295
295
  queryParams: {
296
296
  filters: pivotFilters,
@@ -302,31 +302,31 @@ export default async function getRelatedMethod ({ params, context, vars, helpers
302
302
  transaction: context.transaction,
303
303
  simplified: false,
304
304
  isTransport: params.isTransport
305
- });
306
- let includedResources = pivotResult.included?.filter((r) => r.type === targetType) || [];
305
+ })
306
+ let includedResources = pivotResult.included?.filter((r) => r.type === targetType) || []
307
307
 
308
308
  if (includedResources.length === 0) {
309
- const pivotData = pivotResult.data || [];
309
+ const pivotData = pivotResult.data || []
310
310
  const relatedIds = [...new Set(pivotData
311
311
  .map((item) => {
312
- const attrId = item?.attributes?.[otherKey];
312
+ const attrId = item?.attributes?.[otherKey]
313
313
  if (attrId !== null && attrId !== undefined) {
314
- return String(attrId);
314
+ return String(attrId)
315
315
  }
316
- const relationships = item?.relationships || {};
316
+ const relationships = item?.relationships || {}
317
317
  for (const rel of Object.values(relationships)) {
318
- const data = rel?.data;
318
+ const data = rel?.data
319
319
  if (data?.type === targetType && data?.id != null) {
320
- return String(data.id);
320
+ return String(data.id)
321
321
  }
322
322
  }
323
- return null;
323
+ return null
324
324
  })
325
325
  .filter((id) => id !== null)
326
- )];
326
+ )]
327
327
 
328
328
  if (relatedIds.length > 0) {
329
- includedResources = [];
329
+ includedResources = []
330
330
  for (const relatedId of relatedIds) {
331
331
  const related = await api.resources[targetType].get({
332
332
  id: relatedId,
@@ -334,9 +334,9 @@ export default async function getRelatedMethod ({ params, context, vars, helpers
334
334
  transaction: context.transaction,
335
335
  simplified: false,
336
336
  isTransport: params.isTransport,
337
- });
337
+ })
338
338
  if (related?.data) {
339
- includedResources.push(related.data);
339
+ includedResources.push(related.data)
340
340
  }
341
341
  }
342
342
  }
@@ -348,6 +348,6 @@ export default async function getRelatedMethod ({ params, context, vars, helpers
348
348
  },
349
349
  data: includedResources,
350
350
  meta: pivotResult.meta
351
- };
351
+ }
352
352
  }
353
353
  }
@@ -1,36 +1,35 @@
1
-
2
- import { RestApiResourceError } from "../../../lib/rest-api-errors.js";
3
- import { findRelationshipDefinition } from "./common.js";
4
- import { buildRelationshipUrl } from "../lib/querying/url-helpers.js";
1
+ import { RestApiResourceError } from '../../../lib/rest-api-errors.js'
2
+ import { findRelationshipDefinition } from './common.js'
3
+ import { buildRelationshipUrl } from '../lib/querying/url-helpers.js'
5
4
 
6
5
  /**
7
6
  * GET RELATIONSHIP
8
7
  * Retrieves relationship linkage data (just resource identifiers)
9
8
  * GET /api/articles/1/relationships/author
10
- *
9
+ *
11
10
  * @param {string} id - The ID of the resource
12
11
  * @param {string} relationshipName - The name of the relationship
13
12
  * @returns {Promise<object>} Relationship linkage with links
14
13
  */
15
14
  export default async function getRelationshipMethod ({ params, context, vars, helpers, scope, scopes, runHooks, scopeName, api }) {
16
- context.method = 'getRelationship';
17
- context.id = params.id;
18
- context.relationshipName = params.relationshipName;
19
- context.schemaInfo = scopes[scopeName].vars.schemaInfo;
15
+ context.method = 'getRelationship'
16
+ context.id = params.id
17
+ context.relationshipName = params.relationshipName
18
+ context.schemaInfo = scopes[scopeName].vars.schemaInfo
20
19
 
21
20
  // Validate the relationship exists
22
- const relDef = findRelationshipDefinition(context.schemaInfo, context.relationshipName);
21
+ const relDef = findRelationshipDefinition(context.schemaInfo, context.relationshipName)
23
22
 
24
23
  if (!relDef) {
25
24
  throw new RestApiResourceError(
26
25
  `Relationship '${context.relationshipName}' not found on resource '${scopeName}'`,
27
26
  { subtype: 'relationship_not_found' }
28
- );
27
+ )
29
28
  }
30
29
 
31
30
  // Check permissions
32
- await runHooks('checkPermissions');
33
- await runHooks('checkPermissionsGetRelationship');
31
+ await runHooks('checkPermissions')
32
+ await runHooks('checkPermissionsGetRelationship')
34
33
 
35
34
  // Reuse existing get method with minimal fields
36
35
  const fullRecord = await scope.get({
@@ -42,14 +41,14 @@ export default async function getRelationshipMethod ({ params, context, vars, he
42
41
  transaction: context.transaction,
43
42
  simplified: false,
44
43
  isTransport: params.isTransport
45
- });
44
+ })
46
45
 
47
46
  if (!fullRecord || !fullRecord.data) {
48
- throw new RestApiResourceError('Resource not found', { subtype: 'not_found' });
47
+ throw new RestApiResourceError('Resource not found', { subtype: 'not_found' })
49
48
  }
50
49
 
51
50
  // Extract just the relationship data
52
- const relationshipData = fullRecord.data.relationships?.[context.relationshipName];
51
+ const relationshipData = fullRecord.data.relationships?.[context.relationshipName]
53
52
 
54
53
  // Build response with links
55
54
  return {
@@ -58,6 +57,5 @@ export default async function getRelationshipMethod ({ params, context, vars, he
58
57
  related: buildRelationshipUrl(context, scope, scopeName, context.id, context.relationshipName, false)
59
58
  },
60
59
  data: relationshipData?.data || (relDef.type === 'hasMany' || relDef.type === 'manyToMany' ? [] : null)
61
- };
60
+ }
62
61
  };
63
-