@klerick/json-api-nestjs-sdk 10.0.0-beta.8 → 10.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/CHANGELOG.md +121 -0
- package/README.md +498 -45
- package/cjs/src/index.js +9 -7
- package/cjs/src/index.js.map +1 -1
- package/cjs/src/lib/json-api-angular.js +22 -18
- package/cjs/src/lib/json-api-angular.js.map +1 -1
- package/cjs/src/lib/json-api-js.js +31 -11
- package/cjs/src/lib/json-api-js.js.map +1 -1
- package/cjs/src/lib/service/atomic-operations.service.js +22 -16
- package/cjs/src/lib/service/atomic-operations.service.js.map +1 -1
- package/cjs/src/lib/service/fetch-inner-client.js +3 -3
- package/cjs/src/lib/service/fetch-inner-client.js.map +1 -1
- package/cjs/src/lib/service/index.js +3 -3
- package/cjs/src/lib/service/index.js.map +1 -1
- package/cjs/src/lib/service/json-api-sdk.service.js +55 -22
- package/cjs/src/lib/service/json-api-sdk.service.js.map +1 -1
- package/cjs/src/lib/service/json-api-utils.service.js +138 -59
- package/cjs/src/lib/service/json-api-utils.service.js.map +1 -1
- package/cjs/src/lib/types/entity-chain.js +3 -0
- package/cjs/src/lib/types/entity-chain.js.map +1 -0
- package/cjs/src/lib/types/index.js +10 -9
- package/cjs/src/lib/types/index.js.map +1 -1
- package/cjs/src/lib/utils/generate-atomic-body.js +21 -20
- package/cjs/src/lib/utils/generate-atomic-body.js.map +1 -1
- package/cjs/src/lib/utils/index.js +5 -5
- package/cjs/src/lib/utils/index.js.map +1 -1
- package/cjs/src/lib/utils/utils.js +42 -3
- package/cjs/src/lib/utils/utils.js.map +1 -1
- package/cjs/src/ngModule.js +8 -8
- package/cjs/src/ngModule.js.map +1 -1
- package/mjs/package.json +1 -0
- package/mjs/src/index.d.ts +4 -4
- package/mjs/src/index.js +3 -3
- package/mjs/src/index.js.map +1 -1
- package/mjs/src/lib/json-api-angular.d.ts +10 -12
- package/mjs/src/lib/json-api-angular.js +11 -7
- package/mjs/src/lib/json-api-angular.js.map +1 -1
- package/mjs/src/lib/json-api-js.d.ts +4 -4
- package/mjs/src/lib/json-api-js.js +26 -6
- package/mjs/src/lib/json-api-js.js.map +1 -1
- package/mjs/src/lib/service/atomic-operations.service.d.ts +7 -7
- package/mjs/src/lib/service/atomic-operations.service.js +21 -15
- package/mjs/src/lib/service/atomic-operations.service.js.map +1 -1
- package/mjs/src/lib/service/fetch-inner-client.d.ts +2 -2
- package/mjs/src/lib/service/fetch-inner-client.js +1 -1
- package/mjs/src/lib/service/fetch-inner-client.js.map +1 -1
- package/mjs/src/lib/service/index.d.ts +3 -3
- package/mjs/src/lib/service/index.js +3 -3
- package/mjs/src/lib/service/index.js.map +1 -1
- package/mjs/src/lib/service/json-api-sdk.service.d.ts +13 -8
- package/mjs/src/lib/service/json-api-sdk.service.js +51 -18
- package/mjs/src/lib/service/json-api-sdk.service.js.map +1 -1
- package/mjs/src/lib/service/json-api-utils.service.d.ts +6 -5
- package/mjs/src/lib/service/json-api-utils.service.js +132 -53
- package/mjs/src/lib/service/json-api-utils.service.js.map +1 -1
- package/mjs/src/lib/token/index.d.ts +1 -1
- package/mjs/src/lib/types/atomic-operation.d.ts +11 -11
- package/mjs/src/lib/types/atomic-type.d.ts +2 -2
- package/mjs/src/lib/types/config.d.ts +2 -2
- package/mjs/src/lib/types/entity-chain.d.ts +32 -0
- package/mjs/src/lib/types/entity-chain.js +1 -0
- package/mjs/src/lib/types/entity-chain.js.map +1 -0
- package/mjs/src/lib/types/http-inner-client.d.ts +3 -3
- package/mjs/src/lib/types/http-request-params.d.ts +3 -0
- package/mjs/src/lib/types/index.d.ts +10 -9
- package/mjs/src/lib/types/index.js +10 -9
- package/mjs/src/lib/types/index.js.map +1 -1
- package/mjs/src/lib/types/promise-json-api-sdk.d.ts +20 -9
- package/mjs/src/lib/types/query-params.d.ts +1 -1
- package/mjs/src/lib/utils/adapter-for-axios.d.ts +1 -1
- package/mjs/src/lib/utils/generate-atomic-body.d.ts +7 -7
- package/mjs/src/lib/utils/generate-atomic-body.js +19 -18
- package/mjs/src/lib/utils/generate-atomic-body.js.map +1 -1
- package/mjs/src/lib/utils/index.d.ts +5 -5
- package/mjs/src/lib/utils/index.js +5 -5
- package/mjs/src/lib/utils/index.js.map +1 -1
- package/mjs/src/lib/utils/utils.d.ts +20 -1
- package/mjs/src/lib/utils/utils.js +36 -1
- package/mjs/src/lib/utils/utils.js.map +1 -1
- package/mjs/src/ngModule.d.ts +3 -3
- package/mjs/src/ngModule.js +2 -2
- package/mjs/src/ngModule.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ Type-safe TypeScript/JavaScript client for consuming [JSON:API](https://jsonapi.
|
|
|
16
16
|
- 🔍 **Advanced Filtering** - Rich query builder with operators (eq, ne, in, like, gt, lt, etc.)
|
|
17
17
|
- 📦 **Relationship Handling** - Easy include, sparse fieldsets, and relationship management
|
|
18
18
|
- ⚡ **Atomic Operations** - Batch multiple operations in a single request with rollback support
|
|
19
|
+
- 📤 **Meta Support** - Pass additional business logic data with requests (JSON:API spec compliant)
|
|
19
20
|
- 🌐 **Multiple HTTP Clients** - Works with Axios, Fetch API, and Angular HttpClient
|
|
20
21
|
- 📄 **Pagination & Sorting** - Built-in support for pagination and multi-field sorting
|
|
21
22
|
- 🔄 **Observable or Promise** - Choose your async style (RxJS Observable or native Promise)
|
|
@@ -34,6 +35,11 @@ Type-safe TypeScript/JavaScript client for consuming [JSON:API](https://jsonapi.
|
|
|
34
35
|
- [Updating Resources](#updating-resources)
|
|
35
36
|
- [Deleting Resources](#deleting-resources)
|
|
36
37
|
- [Relationship Operations](#relationship-operations)
|
|
38
|
+
- [Working with Plain Objects](#-working-with-plain-objects)
|
|
39
|
+
- [Using entity() Method](#using-entity-method)
|
|
40
|
+
- [Using String Type Names](#using-string-type-names)
|
|
41
|
+
- [Nullifying Relationships](#-nullifying-relationships)
|
|
42
|
+
- [Clearing To-Many Relationships](#-clearing-to-many-relationships)
|
|
37
43
|
- [Query Options](#-query-options)
|
|
38
44
|
- [Filtering](#filtering)
|
|
39
45
|
- [Sorting](#sorting)
|
|
@@ -41,6 +47,10 @@ Type-safe TypeScript/JavaScript client for consuming [JSON:API](https://jsonapi.
|
|
|
41
47
|
- [Including Relationships](#including-relationships)
|
|
42
48
|
- [Sparse Fieldsets](#sparse-fieldsets)
|
|
43
49
|
- [Atomic Operations](#-atomic-operations)
|
|
50
|
+
- [Meta Support](#-meta-support)
|
|
51
|
+
- [Regular Operations with Meta](#regular-operations-with-meta)
|
|
52
|
+
- [Entity Chain with Meta](#entity-chain-with-meta)
|
|
53
|
+
- [Atomic Operations with Meta](#atomic-operations-with-meta)
|
|
44
54
|
- [Examples](#-examples)
|
|
45
55
|
|
|
46
56
|
|
|
@@ -78,10 +88,10 @@ const jsonSdk = JsonApiJs(
|
|
|
78
88
|
|
|
79
89
|
// 3. Use SDK
|
|
80
90
|
// Fetch all users
|
|
81
|
-
const users = await jsonSdk.
|
|
91
|
+
const users = await jsonSdk.jsonApiSdkService.getAll(Users);
|
|
82
92
|
|
|
83
93
|
// Fetch with filtering and relationships
|
|
84
|
-
const activeUsers = await jsonSdk.
|
|
94
|
+
const activeUsers = await jsonSdk.jsonApiSdkService.getAll(Users, {
|
|
85
95
|
filter: {
|
|
86
96
|
target: {
|
|
87
97
|
isActive: { [FilterOperand.eq]: 'true' }
|
|
@@ -91,7 +101,7 @@ const activeUsers = await jsonSdk.jonApiSdkService.getAll(Users, {
|
|
|
91
101
|
});
|
|
92
102
|
|
|
93
103
|
// Get one user
|
|
94
|
-
const user = await jsonSdk.
|
|
104
|
+
const user = await jsonSdk.jsonApiSdkService.getOne(Users, '1', {
|
|
95
105
|
include: ['addresses', 'comments', 'roles', 'manager']
|
|
96
106
|
});
|
|
97
107
|
|
|
@@ -102,14 +112,14 @@ newUser.lastName = 'Doe';
|
|
|
102
112
|
newUser.login = 'johndoe';
|
|
103
113
|
newUser.isActive = true;
|
|
104
114
|
|
|
105
|
-
const createdUser = await jsonSdk.
|
|
115
|
+
const createdUser = await jsonSdk.jsonApiSdkService.postOne(newUser);
|
|
106
116
|
|
|
107
117
|
// Update a user
|
|
108
118
|
createdUser.firstName = 'Jane';
|
|
109
|
-
const updatedUser = await jsonSdk.
|
|
119
|
+
const updatedUser = await jsonSdk.jsonApiSdkService.patchOne(createdUser);
|
|
110
120
|
|
|
111
121
|
// Delete a user
|
|
112
|
-
await jsonSdk.
|
|
122
|
+
await jsonSdk.jsonApiSdkService.deleteOne(createdUser);
|
|
113
123
|
```
|
|
114
124
|
|
|
115
125
|
### Angular Setup
|
|
@@ -128,6 +138,8 @@ import { Component, inject } from '@angular/core';
|
|
|
128
138
|
import { bootstrapApplication } from '@angular/platform-browser';
|
|
129
139
|
|
|
130
140
|
// 1. Configure in your main.ts or app.config.ts
|
|
141
|
+
|
|
142
|
+
// Option A: Direct configuration object
|
|
131
143
|
const angularConfig = {
|
|
132
144
|
apiHost: 'http://localhost:3000',
|
|
133
145
|
idKey: 'id',
|
|
@@ -143,6 +155,23 @@ bootstrapApplication(AppComponent, {
|
|
|
143
155
|
],
|
|
144
156
|
}).catch((err) => console.error(err));
|
|
145
157
|
|
|
158
|
+
// Option B: Factory function (useful for dynamic configuration)
|
|
159
|
+
bootstrapApplication(AppComponent, {
|
|
160
|
+
providers: [
|
|
161
|
+
provideHttpClient(withFetch()),
|
|
162
|
+
provideJsonApi(() => {
|
|
163
|
+
const env = inject(EnvironmentService);
|
|
164
|
+
return {
|
|
165
|
+
apiHost: env.apiUrl,
|
|
166
|
+
idKey: 'id',
|
|
167
|
+
apiPrefix: 'api',
|
|
168
|
+
operationUrl: 'operation',
|
|
169
|
+
dateFields: ['createdAt', 'updatedAt']
|
|
170
|
+
};
|
|
171
|
+
})
|
|
172
|
+
],
|
|
173
|
+
}).catch((err) => console.error(err));
|
|
174
|
+
|
|
146
175
|
// 2. Use in your components
|
|
147
176
|
@Component({
|
|
148
177
|
standalone: true,
|
|
@@ -186,6 +215,10 @@ type JsonSdkConfig = {
|
|
|
186
215
|
type JsonConfig = JsonSdkConfig & {
|
|
187
216
|
adapter?: HttpInnerClient; // HTTP client adapter (default: fetch)
|
|
188
217
|
}
|
|
218
|
+
|
|
219
|
+
// Angular: provideJsonApi accepts config or factory function
|
|
220
|
+
type JsonSdkConfigFactory = () => JsonSdkConfig;
|
|
221
|
+
type JsonSdkConfigOrFactory = JsonSdkConfig | JsonSdkConfigFactory;
|
|
189
222
|
```
|
|
190
223
|
|
|
191
224
|
### HTTP Adapters
|
|
@@ -225,10 +258,10 @@ Fetch all resources with optional filtering, sorting, and relationships.
|
|
|
225
258
|
import { FilterOperand } from '@klerick/json-api-nestjs-sdk';
|
|
226
259
|
|
|
227
260
|
// Fetch all users
|
|
228
|
-
const users = await jsonSdk.
|
|
261
|
+
const users = await jsonSdk.jsonApiSdkService.getAll(Users);
|
|
229
262
|
|
|
230
263
|
// With filtering
|
|
231
|
-
const activeUsers = await jsonSdk.
|
|
264
|
+
const activeUsers = await jsonSdk.jsonApiSdkService.getAll(Users, {
|
|
232
265
|
filter: {
|
|
233
266
|
target: {
|
|
234
267
|
isActive: { [FilterOperand.eq]: 'true' },
|
|
@@ -239,7 +272,7 @@ const activeUsers = await jsonSdk.jonApiSdkService.getAll(Users, {
|
|
|
239
272
|
});
|
|
240
273
|
|
|
241
274
|
// Filter by relationship
|
|
242
|
-
const usersWithRoles = await jsonSdk.
|
|
275
|
+
const usersWithRoles = await jsonSdk.jsonApiSdkService.getAll(Users, {
|
|
243
276
|
filter: {
|
|
244
277
|
target: {
|
|
245
278
|
id: { [FilterOperand.in]: ['1', '2'] }
|
|
@@ -257,7 +290,7 @@ const usersWithRoles = await jsonSdk.jonApiSdkService.getAll(Users, {
|
|
|
257
290
|
Fetch resources with pagination (returns paginated results).
|
|
258
291
|
|
|
259
292
|
```typescript
|
|
260
|
-
const firstPage = await jsonSdk.
|
|
293
|
+
const firstPage = await jsonSdk.jsonApiSdkService.getList(Users, {
|
|
261
294
|
page: {
|
|
262
295
|
number: 1,
|
|
263
296
|
size: 10
|
|
@@ -269,7 +302,7 @@ const firstPage = await jsonSdk.jonApiSdkService.getList(Users, {
|
|
|
269
302
|
}
|
|
270
303
|
});
|
|
271
304
|
|
|
272
|
-
const secondPage = await jsonSdk.
|
|
305
|
+
const secondPage = await jsonSdk.jsonApiSdkService.getList(Users, {
|
|
273
306
|
page: {
|
|
274
307
|
number: 2,
|
|
275
308
|
size: 10
|
|
@@ -288,15 +321,15 @@ Fetch a single resource by ID.
|
|
|
288
321
|
|
|
289
322
|
```typescript
|
|
290
323
|
// Simple fetch
|
|
291
|
-
const user = await jsonSdk.
|
|
324
|
+
const user = await jsonSdk.jsonApiSdkService.getOne(Users, '1');
|
|
292
325
|
|
|
293
326
|
// With relationships
|
|
294
|
-
const userWithRelations = await jsonSdk.
|
|
327
|
+
const userWithRelations = await jsonSdk.jsonApiSdkService.getOne(Users, '1', {
|
|
295
328
|
include: ['addresses', 'comments', 'roles', 'manager']
|
|
296
329
|
});
|
|
297
330
|
|
|
298
331
|
// With sparse fieldsets
|
|
299
|
-
const userPartial = await jsonSdk.
|
|
332
|
+
const userPartial = await jsonSdk.jsonApiSdkService.getOne(Users, '1', {
|
|
300
333
|
fields: {
|
|
301
334
|
users: ['firstName', 'lastName', 'email']
|
|
302
335
|
}
|
|
@@ -317,7 +350,17 @@ newUser.lastName = 'Doe';
|
|
|
317
350
|
newUser.login = 'johndoe';
|
|
318
351
|
newUser.isActive = true;
|
|
319
352
|
|
|
320
|
-
const createdUser = await jsonSdk.
|
|
353
|
+
const createdUser = await jsonSdk.jsonApiSdkService.postOne(newUser);
|
|
354
|
+
|
|
355
|
+
// Create with client-generated ID
|
|
356
|
+
// Note: Server must have `allowSetId: true` option enabled
|
|
357
|
+
const userWithId = new Users();
|
|
358
|
+
userWithId.id = 'my-custom-uuid';
|
|
359
|
+
userWithId.firstName = 'Jane';
|
|
360
|
+
userWithId.lastName = 'Doe';
|
|
361
|
+
userWithId.login = 'janedoe';
|
|
362
|
+
|
|
363
|
+
const createdUserWithId = await jsonSdk.jsonApiSdkService.postOne(userWithId);
|
|
321
364
|
|
|
322
365
|
// Create with relationships
|
|
323
366
|
const newAddress = new Addresses();
|
|
@@ -325,7 +368,7 @@ newAddress.city = 'New York';
|
|
|
325
368
|
newAddress.state = 'NY';
|
|
326
369
|
newAddress.country = 'USA';
|
|
327
370
|
|
|
328
|
-
const savedAddress = await jsonSdk.
|
|
371
|
+
const savedAddress = await jsonSdk.jsonApiSdkService.postOne(newAddress);
|
|
329
372
|
|
|
330
373
|
const user = new Users();
|
|
331
374
|
user.firstName = 'Jane';
|
|
@@ -333,7 +376,7 @@ user.lastName = 'Doe';
|
|
|
333
376
|
user.login = 'janedoe';
|
|
334
377
|
user.addresses = savedAddress; // Set relationship
|
|
335
378
|
|
|
336
|
-
const createdUser = await jsonSdk.
|
|
379
|
+
const createdUser = await jsonSdk.jsonApiSdkService.postOne(user);
|
|
337
380
|
```
|
|
338
381
|
|
|
339
382
|
### Updating Resources
|
|
@@ -345,13 +388,13 @@ Update an existing resource.
|
|
|
345
388
|
```typescript
|
|
346
389
|
// Update attributes
|
|
347
390
|
user.firstName = 'Updated Name';
|
|
348
|
-
const updatedUser = await jsonSdk.
|
|
391
|
+
const updatedUser = await jsonSdk.jsonApiSdkService.patchOne(user);
|
|
349
392
|
|
|
350
393
|
// Update relationships
|
|
351
|
-
const newAddress = await jsonSdk.
|
|
394
|
+
const newAddress = await jsonSdk.jsonApiSdkService.postOne(addressEntity);
|
|
352
395
|
user.addresses = newAddress;
|
|
353
396
|
|
|
354
|
-
const updatedUser = await jsonSdk.
|
|
397
|
+
const updatedUser = await jsonSdk.jsonApiSdkService.patchOne(user);
|
|
355
398
|
```
|
|
356
399
|
|
|
357
400
|
### Deleting Resources
|
|
@@ -361,7 +404,7 @@ const updatedUser = await jsonSdk.jonApiSdkService.patchOne(user);
|
|
|
361
404
|
Delete a resource.
|
|
362
405
|
|
|
363
406
|
```typescript
|
|
364
|
-
await jsonSdk.
|
|
407
|
+
await jsonSdk.jsonApiSdkService.deleteOne(user);
|
|
365
408
|
```
|
|
366
409
|
|
|
367
410
|
### Relationship Operations
|
|
@@ -372,13 +415,196 @@ Remove relationships without deleting the related resources.
|
|
|
372
415
|
|
|
373
416
|
```typescript
|
|
374
417
|
// Remove all roles from user
|
|
375
|
-
await jsonSdk.
|
|
418
|
+
await jsonSdk.jsonApiSdkService.deleteRelationships(user, 'roles');
|
|
376
419
|
|
|
377
420
|
// Remove manager from user
|
|
378
|
-
await jsonSdk.
|
|
421
|
+
await jsonSdk.jsonApiSdkService.deleteRelationships(user, 'manager');
|
|
379
422
|
|
|
380
423
|
// Remove all comments from user
|
|
381
|
-
await jsonSdk.
|
|
424
|
+
await jsonSdk.jsonApiSdkService.deleteRelationships(user, 'comments');
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
## 🏗️ Working with Plain Objects
|
|
430
|
+
|
|
431
|
+
In monorepo environments or when sharing types between frontend and backend, you may want to use plain TypeScript types/interfaces instead of classes. The SDK provides tools to work with plain objects while maintaining full type safety.
|
|
432
|
+
|
|
433
|
+
### Using entity() Method
|
|
434
|
+
|
|
435
|
+
The `entity()` method creates a properly typed entity instance from a plain object. This is essential when:
|
|
436
|
+
- You share types (not classes) between frontend and backend
|
|
437
|
+
- The SDK needs to identify the resource type at runtime (via `constructor.name`)
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
import { JsonApiJs } from '@klerick/json-api-nestjs-sdk';
|
|
441
|
+
|
|
442
|
+
// Shared type (not a class)
|
|
443
|
+
interface User {
|
|
444
|
+
id?: number;
|
|
445
|
+
firstName: string;
|
|
446
|
+
lastName: string;
|
|
447
|
+
login: string;
|
|
448
|
+
manager?: User | null;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const jsonSdk = JsonApiJs({ apiHost: 'http://localhost:3000', apiPrefix: 'api' }, true);
|
|
452
|
+
|
|
453
|
+
// Create entity from plain object - chainable API
|
|
454
|
+
const createdUser = await jsonSdk.jsonApiSdkService
|
|
455
|
+
.entity<User>('Users', {
|
|
456
|
+
firstName: 'John',
|
|
457
|
+
lastName: 'Doe',
|
|
458
|
+
login: 'johndoe'
|
|
459
|
+
})
|
|
460
|
+
.postOne();
|
|
461
|
+
|
|
462
|
+
// Update entity - chainable API
|
|
463
|
+
const updatedUser = await jsonSdk.jsonApiSdkService
|
|
464
|
+
.entity<User>('Users', {
|
|
465
|
+
id: 1,
|
|
466
|
+
firstName: 'Jane'
|
|
467
|
+
})
|
|
468
|
+
.patchOne();
|
|
469
|
+
|
|
470
|
+
// Delete entity - chainable API
|
|
471
|
+
await jsonSdk.jsonApiSdkService
|
|
472
|
+
.entity<User>('Users', { id: 1 })
|
|
473
|
+
.deleteOne();
|
|
474
|
+
|
|
475
|
+
// Work with relationships
|
|
476
|
+
const userRelations = await jsonSdk.jsonApiSdkService
|
|
477
|
+
.entity<User>('Users', { id: 1 })
|
|
478
|
+
.getRelationships('manager');
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
**Raw mode** - get the entity instance without chaining:
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
// Get raw entity instance (third argument = true)
|
|
485
|
+
const userEntity = jsonSdk.jsonApiSdkService.entity<User>('Users', {
|
|
486
|
+
firstName: 'John',
|
|
487
|
+
lastName: 'Doe',
|
|
488
|
+
login: 'johndoe'
|
|
489
|
+
}, true);
|
|
490
|
+
|
|
491
|
+
// Now use it with standard SDK methods
|
|
492
|
+
const created = await jsonSdk.jsonApiSdkService.postOne(userEntity);
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### Using String Type Names
|
|
496
|
+
|
|
497
|
+
GET methods also accept string type names instead of classes:
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
// Using string type name
|
|
501
|
+
const users = await jsonSdk.jsonApiSdkService.getAll<User>('Users', {
|
|
502
|
+
include: ['manager']
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
const user = await jsonSdk.jsonApiSdkService.getOne<User>('Users', '1', {
|
|
506
|
+
include: ['manager']
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
const userList = await jsonSdk.jsonApiSdkService.getList<User>('Users', {
|
|
510
|
+
page: { number: 1, size: 10 }
|
|
511
|
+
});
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## 🔗 Nullifying Relationships
|
|
517
|
+
|
|
518
|
+
To clear a relationship (set it to `null`), use the `nullRef()` function. This is necessary because the SDK distinguishes between:
|
|
519
|
+
- **Missing relationship** - not included in the request (no change)
|
|
520
|
+
- **Null relationship** - explicitly set to `null` (clear the relationship)
|
|
521
|
+
|
|
522
|
+
```typescript
|
|
523
|
+
import { JsonApiJs, nullRef } from '@klerick/json-api-nestjs-sdk';
|
|
524
|
+
|
|
525
|
+
interface User {
|
|
526
|
+
id?: number;
|
|
527
|
+
firstName: string;
|
|
528
|
+
manager?: User | null;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const jsonSdk = JsonApiJs({ apiHost: 'http://localhost:3000', apiPrefix: 'api' }, true);
|
|
532
|
+
|
|
533
|
+
// Clear the manager relationship
|
|
534
|
+
const user = jsonSdk.jsonApiSdkService.entity<User>('Users', {
|
|
535
|
+
id: 1,
|
|
536
|
+
firstName: 'John',
|
|
537
|
+
manager: nullRef() // This will send { data: null } for the relationship
|
|
538
|
+
}, true);
|
|
539
|
+
|
|
540
|
+
const updatedUser = await jsonSdk.jsonApiSdkService.patchOne(user);
|
|
541
|
+
// Result: user.manager is now null
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
**How it works:**
|
|
545
|
+
- `nullRef()` returns a special marker object that TypeScript sees as `null`
|
|
546
|
+
- At runtime, the SDK detects this marker and generates `{ data: null }` in the JSON:API request body
|
|
547
|
+
- The server then clears the relationship
|
|
548
|
+
|
|
549
|
+
**Without nullRef:**
|
|
550
|
+
```typescript
|
|
551
|
+
// This won't clear the relationship - it will be ignored
|
|
552
|
+
const user = jsonSdk.jsonApiSdkService.entity<User>('Users', {
|
|
553
|
+
id: 1,
|
|
554
|
+
firstName: 'John',
|
|
555
|
+
// manager is undefined - not included in request
|
|
556
|
+
}, true);
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
**With nullRef:**
|
|
560
|
+
```typescript
|
|
561
|
+
// This explicitly clears the relationship
|
|
562
|
+
const user = jsonSdk.jsonApiSdkService.entity<User>('Users', {
|
|
563
|
+
id: 1,
|
|
564
|
+
firstName: 'John',
|
|
565
|
+
manager: nullRef() // Generates: relationships: { manager: { data: null } }
|
|
566
|
+
}, true);
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
## 🔗 Clearing To-Many Relationships
|
|
570
|
+
|
|
571
|
+
To clear all items from a to-many relationship (ManyToMany, OneToMany), use the `emptyArrayRef()` function:
|
|
572
|
+
|
|
573
|
+
```typescript
|
|
574
|
+
import { JsonApiJs, emptyArrayRef } from '@klerick/json-api-nestjs-sdk';
|
|
575
|
+
|
|
576
|
+
interface User {
|
|
577
|
+
id?: number;
|
|
578
|
+
firstName: string;
|
|
579
|
+
roles?: Role[];
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const jsonSdk = JsonApiJs({ apiHost: 'http://localhost:3000', apiPrefix: 'api' }, true);
|
|
583
|
+
|
|
584
|
+
// Clear all roles from user
|
|
585
|
+
const user = jsonSdk.jsonApiSdkService.entity<User>('Users', {
|
|
586
|
+
id: 1,
|
|
587
|
+
firstName: 'John',
|
|
588
|
+
roles: emptyArrayRef() // This will send { data: [] } for the relationship
|
|
589
|
+
}, true);
|
|
590
|
+
|
|
591
|
+
const updatedUser = await jsonSdk.jsonApiSdkService.patchOne(user);
|
|
592
|
+
// Result: user.roles is now an empty array
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
**Why emptyArrayRef is needed:**
|
|
596
|
+
- An empty array `[]` would be treated as an attribute (not a relationship)
|
|
597
|
+
- `emptyArrayRef()` marks it as a relationship that should be cleared
|
|
598
|
+
- At runtime, the SDK generates `{ data: [] }` in the JSON:API request body
|
|
599
|
+
|
|
600
|
+
**Comparison:**
|
|
601
|
+
```typescript
|
|
602
|
+
// ❌ This won't work - empty array treated as attribute
|
|
603
|
+
const user = { id: 1, roles: [] };
|
|
604
|
+
|
|
605
|
+
// ✅ This works - explicitly clears the relationship
|
|
606
|
+
const user = { id: 1, roles: emptyArrayRef() };
|
|
607
|
+
// Generates: relationships: { roles: { data: [] } }
|
|
382
608
|
```
|
|
383
609
|
|
|
384
610
|
---
|
|
@@ -408,7 +634,7 @@ enum FilterOperand {
|
|
|
408
634
|
|
|
409
635
|
```typescript
|
|
410
636
|
// Equal
|
|
411
|
-
const users = await jsonSdk.
|
|
637
|
+
const users = await jsonSdk.jsonApiSdkService.getAll(Users, {
|
|
412
638
|
filter: {
|
|
413
639
|
target: {
|
|
414
640
|
isActive: { [FilterOperand.eq]: 'true' }
|
|
@@ -417,7 +643,7 @@ const users = await jsonSdk.jonApiSdkService.getAll(Users, {
|
|
|
417
643
|
});
|
|
418
644
|
|
|
419
645
|
// Not equal
|
|
420
|
-
const inactiveUsers = await jsonSdk.
|
|
646
|
+
const inactiveUsers = await jsonSdk.jsonApiSdkService.getAll(Users, {
|
|
421
647
|
filter: {
|
|
422
648
|
target: {
|
|
423
649
|
isActive: { [FilterOperand.ne]: 'true' }
|
|
@@ -426,7 +652,7 @@ const inactiveUsers = await jsonSdk.jonApiSdkService.getAll(Users, {
|
|
|
426
652
|
});
|
|
427
653
|
|
|
428
654
|
// In array
|
|
429
|
-
const specificUsers = await jsonSdk.
|
|
655
|
+
const specificUsers = await jsonSdk.jsonApiSdkService.getAll(Users, {
|
|
430
656
|
filter: {
|
|
431
657
|
target: {
|
|
432
658
|
id: { [FilterOperand.in]: ['1', '2', '3'] }
|
|
@@ -435,7 +661,7 @@ const specificUsers = await jsonSdk.jonApiSdkService.getAll(Users, {
|
|
|
435
661
|
});
|
|
436
662
|
|
|
437
663
|
// LIKE search
|
|
438
|
-
const searchUsers = await jsonSdk.
|
|
664
|
+
const searchUsers = await jsonSdk.jsonApiSdkService.getAll(Users, {
|
|
439
665
|
filter: {
|
|
440
666
|
target: {
|
|
441
667
|
login: { [FilterOperand.like]: 'john' }
|
|
@@ -444,7 +670,7 @@ const searchUsers = await jsonSdk.jonApiSdkService.getAll(Users, {
|
|
|
444
670
|
});
|
|
445
671
|
|
|
446
672
|
// Check null/not null
|
|
447
|
-
const usersWithManager = await jsonSdk.
|
|
673
|
+
const usersWithManager = await jsonSdk.jsonApiSdkService.getAll(Users, {
|
|
448
674
|
filter: {
|
|
449
675
|
target: {
|
|
450
676
|
manager: { [FilterOperand.ne]: null }
|
|
@@ -452,7 +678,7 @@ const usersWithManager = await jsonSdk.jonApiSdkService.getAll(Users, {
|
|
|
452
678
|
}
|
|
453
679
|
});
|
|
454
680
|
|
|
455
|
-
const usersWithoutManager = await jsonSdk.
|
|
681
|
+
const usersWithoutManager = await jsonSdk.jsonApiSdkService.getAll(Users, {
|
|
456
682
|
filter: {
|
|
457
683
|
target: {
|
|
458
684
|
manager: { [FilterOperand.eq]: null }
|
|
@@ -465,7 +691,7 @@ const usersWithoutManager = await jsonSdk.jonApiSdkService.getAll(Users, {
|
|
|
465
691
|
|
|
466
692
|
```typescript
|
|
467
693
|
// Sort by single field
|
|
468
|
-
const users = await jsonSdk.
|
|
694
|
+
const users = await jsonSdk.jsonApiSdkService.getList(Users, {
|
|
469
695
|
sort: {
|
|
470
696
|
target: {
|
|
471
697
|
id: 'ASC'
|
|
@@ -474,7 +700,7 @@ const users = await jsonSdk.jonApiSdkService.getList(Users, {
|
|
|
474
700
|
});
|
|
475
701
|
|
|
476
702
|
// Sort by multiple fields
|
|
477
|
-
const sortedUsers = await jsonSdk.
|
|
703
|
+
const sortedUsers = await jsonSdk.jsonApiSdkService.getList(Users, {
|
|
478
704
|
sort: {
|
|
479
705
|
target: {
|
|
480
706
|
createdAt: 'DESC',
|
|
@@ -487,7 +713,7 @@ const sortedUsers = await jsonSdk.jonApiSdkService.getList(Users, {
|
|
|
487
713
|
### Pagination
|
|
488
714
|
|
|
489
715
|
```typescript
|
|
490
|
-
const paginatedUsers = await jsonSdk.
|
|
716
|
+
const paginatedUsers = await jsonSdk.jsonApiSdkService.getList(Users, {
|
|
491
717
|
page: {
|
|
492
718
|
number: 1, // Page number (1-indexed)
|
|
493
719
|
size: 20 // Items per page
|
|
@@ -499,17 +725,17 @@ const paginatedUsers = await jsonSdk.jonApiSdkService.getList(Users, {
|
|
|
499
725
|
|
|
500
726
|
```typescript
|
|
501
727
|
// Include single relationship
|
|
502
|
-
const users = await jsonSdk.
|
|
728
|
+
const users = await jsonSdk.jsonApiSdkService.getAll(Users, {
|
|
503
729
|
include: ['addresses']
|
|
504
730
|
});
|
|
505
731
|
|
|
506
732
|
// Include multiple relationships
|
|
507
|
-
const usersWithAll = await jsonSdk.
|
|
733
|
+
const usersWithAll = await jsonSdk.jsonApiSdkService.getAll(Users, {
|
|
508
734
|
include: ['addresses', 'roles', 'comments', 'manager']
|
|
509
735
|
});
|
|
510
736
|
|
|
511
737
|
// Include nested relationships
|
|
512
|
-
const usersWithNested = await jsonSdk.
|
|
738
|
+
const usersWithNested = await jsonSdk.jsonApiSdkService.getAll(Users, {
|
|
513
739
|
include: ['addresses', 'manager.addresses', 'roles']
|
|
514
740
|
});
|
|
515
741
|
```
|
|
@@ -519,7 +745,7 @@ const usersWithNested = await jsonSdk.jonApiSdkService.getAll(Users, {
|
|
|
519
745
|
Request only specific fields to reduce payload size.
|
|
520
746
|
|
|
521
747
|
```typescript
|
|
522
|
-
const users = await jsonSdk.
|
|
748
|
+
const users = await jsonSdk.jsonApiSdkService.getAll(Users, {
|
|
523
749
|
fields: {
|
|
524
750
|
users: ['firstName', 'lastName', 'email'],
|
|
525
751
|
addresses: ['city', 'country']
|
|
@@ -614,28 +840,255 @@ const result = await jsonSdk
|
|
|
614
840
|
// result[3] - array of all comment IDs after addition
|
|
615
841
|
```
|
|
616
842
|
|
|
617
|
-
### Using
|
|
843
|
+
### Using Local Identifiers (lid)
|
|
618
844
|
|
|
619
|
-
|
|
845
|
+
Create and reference resources within the same atomic request using local identifiers (`lid`). This allows you to establish relationships between resources being created in a single batch operation.
|
|
620
846
|
|
|
847
|
+
**How it works:**
|
|
848
|
+
1. Assign a temporary ID to the resource (any unique value - number or string)
|
|
849
|
+
2. Reference this ID in relationships of other resources in the same request
|
|
850
|
+
3. Server automatically replaces temporary IDs with real database IDs
|
|
851
|
+
|
|
852
|
+
**Example - Numeric lid:**
|
|
621
853
|
```typescript
|
|
622
854
|
const address = new Addresses();
|
|
623
855
|
address.city = 'Boston';
|
|
624
|
-
address.id = 10000; // Temporary ID
|
|
856
|
+
address.id = 10000; // Temporary ID (lid)
|
|
625
857
|
|
|
626
858
|
const user = new Users();
|
|
627
859
|
user.firstName = 'Alice';
|
|
628
|
-
user.addresses = address; // Reference by
|
|
860
|
+
user.addresses = address; // Reference resource by temporary ID
|
|
629
861
|
|
|
630
862
|
const [createdAddress, createdUser] = await jsonSdk
|
|
631
863
|
.atomicFactory()
|
|
632
|
-
.postOne(address)
|
|
864
|
+
.postOne(address) // First operation: create address with lid=10000
|
|
865
|
+
.postOne(user) // Second operation: reference address via lid
|
|
866
|
+
.run();
|
|
867
|
+
|
|
868
|
+
// Server assigns real IDs and maintains relationships
|
|
869
|
+
console.log(createdAddress.id); // Real ID (e.g., 42)
|
|
870
|
+
console.log(createdUser.addresses.id); // Same real ID (42)
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
**Example - UUID lid (for entities with UUID primary keys):**
|
|
874
|
+
```typescript
|
|
875
|
+
const book1 = new BookList();
|
|
876
|
+
book1.id = '550e8400-e29b-41d4-a716-446655440000'; // Temporary UUID (lid)
|
|
877
|
+
book1.title = 'TypeScript Handbook';
|
|
878
|
+
|
|
879
|
+
const book2 = new BookList();
|
|
880
|
+
book2.id = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; // Another temporary UUID (lid)
|
|
881
|
+
book2.title = 'Advanced Node.js';
|
|
882
|
+
|
|
883
|
+
const user = new Users();
|
|
884
|
+
user.firstName = 'John';
|
|
885
|
+
user.books = [book1, book2]; // Reference both books by their temporary UUIDs
|
|
886
|
+
|
|
887
|
+
const [createdBook1, createdBook2, createdUser] = await jsonSdk
|
|
888
|
+
.atomicFactory()
|
|
889
|
+
.postOne(book1)
|
|
890
|
+
.postOne(book2)
|
|
633
891
|
.postOne(user)
|
|
634
892
|
.run();
|
|
635
893
|
|
|
636
|
-
//
|
|
637
|
-
|
|
638
|
-
console.log(
|
|
894
|
+
// All temporary IDs are used as actual IDs (for UUID fields)
|
|
895
|
+
// or replaced with real IDs (for autoincrement fields)
|
|
896
|
+
console.log(createdBook1.id); // UUID from lid
|
|
897
|
+
console.log(createdBook2.id); // UUID from lid
|
|
898
|
+
console.log(createdUser.books.map(b => b.id)); // Both book UUIDs
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
**Important notes:**
|
|
902
|
+
- Local identifiers (lid) are only valid within a single atomic request
|
|
903
|
+
- The SDK automatically handles lid assignment in the request body
|
|
904
|
+
- For numeric IDs: lid is replaced with the actual database-generated ID
|
|
905
|
+
- For UUID IDs: lid can be used as the actual ID (if server has `allowSetId: true`)
|
|
906
|
+
|
|
907
|
+
---
|
|
908
|
+
|
|
909
|
+
## 📤 Meta Support
|
|
910
|
+
|
|
911
|
+
The SDK supports passing `meta` objects with requests to send additional business logic data that doesn't belong to the entity itself, according to [JSON:API specification](https://jsonapi.org/format/#document-meta).
|
|
912
|
+
|
|
913
|
+
### Regular Operations with Meta
|
|
914
|
+
|
|
915
|
+
```typescript
|
|
916
|
+
// POST with meta
|
|
917
|
+
const newUser = new Users();
|
|
918
|
+
newUser.firstName = 'John';
|
|
919
|
+
newUser.lastName = 'Doe';
|
|
920
|
+
newUser.login = 'johndoe';
|
|
921
|
+
|
|
922
|
+
const createdUser = await jsonSdk.jsonApiSdkService.postOne(newUser, {
|
|
923
|
+
source: 'import',
|
|
924
|
+
batchId: '12345'
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
// Request body:
|
|
928
|
+
{
|
|
929
|
+
"data": {
|
|
930
|
+
"type": "users",
|
|
931
|
+
"attributes": { "firstName": "John", "lastName": "Doe", "login": "johndoe" }
|
|
932
|
+
},
|
|
933
|
+
"meta": {
|
|
934
|
+
"source": "import",
|
|
935
|
+
"batchId": "12345"
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// PATCH with meta
|
|
940
|
+
user.firstName = 'Jane';
|
|
941
|
+
const updatedUser = await jsonSdk.jsonApiSdkService.patchOne(user, {
|
|
942
|
+
updatedBy: 'admin',
|
|
943
|
+
reason: 'name correction'
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
// Relationship operations with meta
|
|
947
|
+
await jsonSdk.jsonApiSdkService.postRelationships(user, 'roles', {
|
|
948
|
+
addedBy: 'admin',
|
|
949
|
+
timestamp: Date.now()
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
await jsonSdk.jsonApiSdkService.patchRelationships(user, 'roles', {
|
|
953
|
+
source: 'sync',
|
|
954
|
+
syncId: 'abc123'
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
await jsonSdk.jsonApiSdkService.deleteRelationships(user, 'roles', {
|
|
958
|
+
removedBy: 'admin',
|
|
959
|
+
reason: 'access revoked'
|
|
960
|
+
});
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
**Important:** `deleteOne()` does NOT support meta because HTTP DELETE for resources has no body according to JSON:API spec. However, `deleteRelationships()` DOES support meta because relationship deletion endpoints use a request body.
|
|
964
|
+
|
|
965
|
+
### Entity Chain with Meta
|
|
966
|
+
|
|
967
|
+
```typescript
|
|
968
|
+
// Chain mode - Observable
|
|
969
|
+
const user = await jsonSdk.jsonApiSdkService
|
|
970
|
+
.entity('Users', {
|
|
971
|
+
firstName: 'John',
|
|
972
|
+
lastName: 'Doe',
|
|
973
|
+
login: 'johndoe'
|
|
974
|
+
})
|
|
975
|
+
.postOne({ source: 'mobile-app', version: '2.0' });
|
|
976
|
+
|
|
977
|
+
// Update with meta
|
|
978
|
+
await jsonSdk.jsonApiSdkService
|
|
979
|
+
.entity('Users', { id: 1, firstName: 'Jane' })
|
|
980
|
+
.patchOne({ updatedBy: 'user-123' });
|
|
981
|
+
|
|
982
|
+
// Relationship operations with meta
|
|
983
|
+
await jsonSdk.jsonApiSdkService
|
|
984
|
+
.entity('Users', user)
|
|
985
|
+
.patchRelationships('roles', { operation: 'bulk-update' });
|
|
986
|
+
```
|
|
987
|
+
|
|
988
|
+
### Atomic Operations with Meta
|
|
989
|
+
|
|
990
|
+
Each operation in an atomic request can have its own meta:
|
|
991
|
+
|
|
992
|
+
```typescript
|
|
993
|
+
const address1 = new Addresses();
|
|
994
|
+
address1.id = 999; // Local identifier (lid)
|
|
995
|
+
address1.city = 'New York';
|
|
996
|
+
|
|
997
|
+
const address2 = new Addresses();
|
|
998
|
+
address2.id = 1000; // Local identifier (lid)
|
|
999
|
+
address2.city = 'Boston';
|
|
1000
|
+
|
|
1001
|
+
const user1 = new Users();
|
|
1002
|
+
user1.firstName = 'Alice';
|
|
1003
|
+
user1.addresses = address1;
|
|
1004
|
+
|
|
1005
|
+
const user2 = new Users();
|
|
1006
|
+
user2.firstName = 'Bob';
|
|
1007
|
+
user2.addresses = address2;
|
|
1008
|
+
|
|
1009
|
+
const [addr1, addr2, createdUser1, createdUser2] = await jsonSdk
|
|
1010
|
+
.atomicFactory()
|
|
1011
|
+
.postOne(address1)
|
|
1012
|
+
.postOne(address2)
|
|
1013
|
+
.postOne(user1, { source: 'import', priority: 'high' }) // User 1 with meta
|
|
1014
|
+
.postOne(user2, { source: 'import', priority: 'normal' }) // User 2 with different meta
|
|
1015
|
+
.run();
|
|
1016
|
+
|
|
1017
|
+
// Request body:
|
|
1018
|
+
{
|
|
1019
|
+
"atomic:operations": [
|
|
1020
|
+
{
|
|
1021
|
+
"op": "add",
|
|
1022
|
+
"ref": { "type": "addresses", "lid": "999" },
|
|
1023
|
+
"data": { "type": "addresses", "attributes": { "city": "New York" } }
|
|
1024
|
+
},
|
|
1025
|
+
{
|
|
1026
|
+
"op": "add",
|
|
1027
|
+
"ref": { "type": "addresses", "lid": "1000" },
|
|
1028
|
+
"data": { "type": "addresses", "attributes": { "city": "Boston" } }
|
|
1029
|
+
},
|
|
1030
|
+
{
|
|
1031
|
+
"op": "add",
|
|
1032
|
+
"ref": { "type": "users" },
|
|
1033
|
+
"data": {
|
|
1034
|
+
"type": "users",
|
|
1035
|
+
"attributes": { "firstName": "Alice" },
|
|
1036
|
+
"relationships": { "addresses": { "data": { "type": "addresses", "id": "999" } } }
|
|
1037
|
+
},
|
|
1038
|
+
"meta": { "source": "import", "priority": "high" } // Meta for operation 3
|
|
1039
|
+
},
|
|
1040
|
+
{
|
|
1041
|
+
"op": "add",
|
|
1042
|
+
"ref": { "type": "users" },
|
|
1043
|
+
"data": {
|
|
1044
|
+
"type": "users",
|
|
1045
|
+
"attributes": { "firstName": "Bob" },
|
|
1046
|
+
"relationships": { "addresses": { "data": { "type": "addresses", "id": "1000" } } }
|
|
1047
|
+
},
|
|
1048
|
+
"meta": { "source": "import", "priority": "normal" } // Meta for operation 4
|
|
1049
|
+
}
|
|
1050
|
+
]
|
|
1051
|
+
}
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
### Meta with Entity Chain in Atomic Operations
|
|
1055
|
+
|
|
1056
|
+
```typescript
|
|
1057
|
+
const addressEntity = jsonSdk.jsonApiSdkService.entity('Addresses', {
|
|
1058
|
+
id: 999,
|
|
1059
|
+
city: 'New York'
|
|
1060
|
+
}, true);
|
|
1061
|
+
|
|
1062
|
+
const userEntity = jsonSdk.jsonApiSdkService.entity('Users', {
|
|
1063
|
+
firstName: 'Alice',
|
|
1064
|
+
addresses: jsonSdk.jsonApiSdkService.entity('Addresses', { id: 999 }, true)
|
|
1065
|
+
}, true);
|
|
1066
|
+
|
|
1067
|
+
const [createdAddress, createdUser] = await jsonSdk
|
|
1068
|
+
.atomicFactory()
|
|
1069
|
+
.postOne(addressEntity)
|
|
1070
|
+
.postOne(userEntity, { source: 'import', batchId: '12345' })
|
|
1071
|
+
.run();
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
### Operations WITHOUT Meta Support
|
|
1075
|
+
|
|
1076
|
+
The following operations do NOT accept meta parameter:
|
|
1077
|
+
|
|
1078
|
+
```typescript
|
|
1079
|
+
// ❌ deleteOne - HTTP DELETE has no body
|
|
1080
|
+
await jsonSdk.jsonApiSdkService.deleteOne(user);
|
|
1081
|
+
// Cannot pass meta here
|
|
1082
|
+
|
|
1083
|
+
// ❌ deleteOne in atomic operations
|
|
1084
|
+
await jsonSdk.atomicFactory()
|
|
1085
|
+
.deleteOne(user) // No meta parameter
|
|
1086
|
+
.run();
|
|
1087
|
+
|
|
1088
|
+
// ✅ But deleteRelationships DOES support meta (has body)
|
|
1089
|
+
await jsonSdk.jsonApiSdkService.deleteRelationships(user, 'roles', {
|
|
1090
|
+
removedBy: 'admin'
|
|
1091
|
+
});
|
|
639
1092
|
```
|
|
640
1093
|
|
|
641
1094
|
---
|