@klerick/json-api-nestjs-sdk 10.0.0-beta.4 → 10.0.0-beta.6

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 (60) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +608 -97
  3. package/cjs/src/index.js +4 -13
  4. package/cjs/src/index.js.map +1 -1
  5. package/cjs/src/lib/constants/index.js +1 -4
  6. package/cjs/src/lib/constants/index.js.map +1 -1
  7. package/cjs/src/lib/json-api-angular.js +19 -26
  8. package/cjs/src/lib/json-api-angular.js.map +1 -1
  9. package/cjs/src/lib/json-api-js.js +13 -16
  10. package/cjs/src/lib/json-api-js.js.map +1 -1
  11. package/cjs/src/lib/service/atomic-operations.service.js +8 -12
  12. package/cjs/src/lib/service/atomic-operations.service.js.map +1 -1
  13. package/cjs/src/lib/service/fetch-inner-client.js +6 -10
  14. package/cjs/src/lib/service/fetch-inner-client.js.map +1 -1
  15. package/cjs/src/lib/service/index.js +3 -6
  16. package/cjs/src/lib/service/index.js.map +1 -1
  17. package/cjs/src/lib/service/json-api-sdk.service.js +29 -33
  18. package/cjs/src/lib/service/json-api-sdk.service.js.map +1 -1
  19. package/cjs/src/lib/service/json-api-utils.service.js +27 -31
  20. package/cjs/src/lib/service/json-api-utils.service.js.map +1 -1
  21. package/cjs/src/lib/token/index.js +3 -6
  22. package/cjs/src/lib/token/index.js.map +1 -1
  23. package/cjs/src/lib/types/atomic-operation.js +0 -2
  24. package/cjs/src/lib/types/atomic-type.js +1 -3
  25. package/cjs/src/lib/types/atomic-type.js.map +1 -1
  26. package/cjs/src/lib/types/config.js +0 -2
  27. package/cjs/src/lib/types/filter-operand.js +1 -3
  28. package/cjs/src/lib/types/filter-operand.js.map +1 -1
  29. package/cjs/src/lib/types/http-inner-client.js +0 -2
  30. package/cjs/src/lib/types/http-request-params.js +0 -2
  31. package/cjs/src/lib/types/index.js +9 -12
  32. package/cjs/src/lib/types/index.js.map +1 -1
  33. package/cjs/src/lib/types/promise-json-api-sdk.js +0 -2
  34. package/cjs/src/lib/types/query-params.js +1 -3
  35. package/cjs/src/lib/types/query-params.js.map +1 -1
  36. package/cjs/src/lib/types/utils.js +0 -2
  37. package/cjs/src/lib/utils/adapter-for-axios.d.ts +2 -2
  38. package/cjs/src/lib/utils/adapter-for-axios.js +5 -8
  39. package/cjs/src/lib/utils/adapter-for-axios.js.map +1 -1
  40. package/cjs/src/lib/utils/entity-array.js +1 -5
  41. package/cjs/src/lib/utils/entity-array.js.map +1 -1
  42. package/cjs/src/lib/utils/generate-atomic-body.js +11 -15
  43. package/cjs/src/lib/utils/generate-atomic-body.js.map +1 -1
  44. package/cjs/src/lib/utils/http-params.js +2 -7
  45. package/cjs/src/lib/utils/http-params.js.map +1 -1
  46. package/cjs/src/lib/utils/index.js +5 -8
  47. package/cjs/src/lib/utils/index.js.map +1 -1
  48. package/cjs/src/lib/utils/utils.js +8 -13
  49. package/cjs/src/lib/utils/utils.js.map +1 -1
  50. package/cjs/src/ngModule.js +3 -13
  51. package/cjs/src/ngModule.js.map +1 -1
  52. package/mjs/src/lib/types/atomic-operation.js +0 -1
  53. package/mjs/src/lib/types/config.js +0 -1
  54. package/mjs/src/lib/types/http-inner-client.js +0 -1
  55. package/mjs/src/lib/types/http-request-params.js +0 -1
  56. package/mjs/src/lib/types/promise-json-api-sdk.js +0 -1
  57. package/mjs/src/lib/types/utils.js +0 -1
  58. package/mjs/src/lib/utils/adapter-for-axios.d.ts +2 -2
  59. package/mjs/src/lib/utils/adapter-for-axios.js.map +1 -1
  60. package/package.json +3 -3
package/README.md CHANGED
@@ -1,154 +1,665 @@
1
1
  <p align='center'>
2
- <a href="https://www.npmjs.com/package/json-api-nestjs-sdk" target="_blank"><img src="https://img.shields.io/npm/v/json-api-nestjs-sdk.svg" alt="NPM Version" /></a>
3
- <a href="https://www.npmjs.com/package/json-api-nestjs-sdk" target="_blank"><img src="https://img.shields.io/npm/l/json-api-nestjs-sdk.svg" alt="Package License" /></a>
4
- <a href="https://www.npmjs.com/package/json-api-nestjs-sdk" target="_blank"><img src="https://img.shields.io/npm/dm/json-api-nestjs-sdk.svg" alt="NPM Downloads" /></a>
2
+ <a href="https://www.npmjs.com/package/@klerick/json-api-nestjs-sdk" target="_blank"><img src="https://img.shields.io/npm/v/@klerick/json-api-nestjs-sdk.svg" alt="NPM Version" /></a>
3
+ <a href="https://www.npmjs.com/package/@klerick/json-api-nestjs-sdk" target="_blank"><img src="https://img.shields.io/npm/l/@klerick/json-api-nestjs-sdk.svg" alt="Package License" /></a>
4
+ <a href="https://www.npmjs.com/package/@klerick/json-api-nestjs-sdk" target="_blank"><img src="https://img.shields.io/npm/dm/json-api-nestjs-sdk.svg" alt="NPM Downloads" /></a>
5
5
  <a href="http://commitizen.github.io/cz-cli/" target="_blank"><img src="https://img.shields.io/badge/commitizen-friendly-brightgreen.svg" alt="Commitizen friendly" /></a>
6
6
  <img src="https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/klerick/02a4c98cf7008fea2af70dc2d50f4cb7/raw/json-api-nestjs-sdk.json" alt="Coverage Badge" />
7
7
  </p>
8
8
 
9
- # json-api-nestjs-sdk
10
-
11
- The plugin of client for help work with JSON API over [json-api-nestjs](https://www.npmjs.com/package/json-api-nestjs)
9
+ # JSON:API Client SDK
10
+
11
+ Type-safe TypeScript/JavaScript client for consuming [JSON:API](https://jsonapi.org/) endpoints built with [@klerick/json-api-nestjs](https://www.npmjs.com/package/@klerick/json-api-nestjs).
12
+
13
+ ## ✨ Features
14
+
15
+ - 🎯 **Full Type Safety** - Complete TypeScript support with type inference from your entities
16
+ - 🔍 **Advanced Filtering** - Rich query builder with operators (eq, ne, in, like, gt, lt, etc.)
17
+ - 📦 **Relationship Handling** - Easy include, sparse fieldsets, and relationship management
18
+ - ⚡ **Atomic Operations** - Batch multiple operations in a single request with rollback support
19
+ - 🌐 **Multiple HTTP Clients** - Works with Axios, Fetch API, and Angular HttpClient
20
+ - 📄 **Pagination & Sorting** - Built-in support for pagination and multi-field sorting
21
+ - 🔄 **Observable or Promise** - Choose your async style (RxJS Observable or native Promise)
22
+ - 🔗 **Relationship Operations** - Post, patch, and delete relationships independently
23
+
24
+ ## 📚 Table of Contents
25
+
26
+ - [Installation](#installation)
27
+ - [Quick Start](#-quick-start)
28
+ - [Basic Setup (Axios)](#basic-setup-axios)
29
+ - [Angular Setup](#angular-setup)
30
+ - [Configuration](#-configuration)
31
+ - [API Methods](#-api-methods)
32
+ - [Fetching Resources](#fetching-resources)
33
+ - [Creating Resources](#creating-resources)
34
+ - [Updating Resources](#updating-resources)
35
+ - [Deleting Resources](#deleting-resources)
36
+ - [Relationship Operations](#relationship-operations)
37
+ - [Query Options](#-query-options)
38
+ - [Filtering](#filtering)
39
+ - [Sorting](#sorting)
40
+ - [Pagination](#pagination)
41
+ - [Including Relationships](#including-relationships)
42
+ - [Sparse Fieldsets](#sparse-fieldsets)
43
+ - [Atomic Operations](#-atomic-operations)
44
+ - [Examples](#-examples)
12
45
 
13
46
 
14
47
  ## Installation
15
48
 
16
- ```bash $
17
- npm install @klerick/json-api-nestjs-sdk
49
+ ```bash
50
+ npm install @klerick/json-api-nestjs-sdk
18
51
  ```
19
52
 
20
- ## Example
53
+ ---
21
54
 
22
- Once the installation process is complete, we can import the **JsonApiJs**.
55
+ ## 🚀 Quick Start
23
56
 
24
- ```typescript
25
- import {
26
- adapterForAxios,
27
- FilterOperand,
28
- JsonApiJs,
29
- JsonSdkPromise,
30
- } from '@klerick/json-api-nestjs-sdk';
31
- import { faker } from '@faker-js/faker';
32
- import axios from 'axios';
57
+ ### Basic Setup (Axios)
33
58
 
34
- import {Users} from 'database'
59
+ ```typescript
60
+ import { JsonApiJs, adapterForAxios, FilterOperand } from '@klerick/json-api-nestjs-sdk';
61
+ import axios from 'axios';
62
+ import { Users } from './entities'; // Your entity classes
35
63
 
64
+ // 1. Create adapter
36
65
  const axiosAdapter = adapterForAxios(axios);
37
66
 
38
- const jsonConfig: JsonConfig = {
39
- adapter: axiosAdapter,
40
- apiHost: 'http://localhost:3000',
41
- apiPrefix: 'api',
42
- dateFields: ['createdAt', 'updatedAt'],
43
- operationUrl: 'operation',
44
- }
45
-
67
+ // 2. Configure SDK
46
68
  const jsonSdk = JsonApiJs(
47
- jsonConfig,
48
- true //by default all methods return Observable but you return promise
69
+ {
70
+ adapter: axiosAdapter,
71
+ apiHost: 'http://localhost:3000',
72
+ apiPrefix: 'api',
73
+ dateFields: ['createdAt', 'updatedAt'],
74
+ operationUrl: 'operation',
75
+ },
76
+ true // true = return Promises, false = return Observables
49
77
  );
50
78
 
51
- await jsonSdk.jonApiSdkService.getAll(Users, {
79
+ // 3. Use SDK
80
+ // Fetch all users
81
+ const users = await jsonSdk.jonApiSdkService.getAll(Users);
82
+
83
+ // Fetch with filtering and relationships
84
+ const activeUsers = await jsonSdk.jonApiSdkService.getAll(Users, {
52
85
  filter: {
53
86
  target: {
54
- id: { [FilterOperand.in]: usersId.map((i) => `${i}`) },
55
- },
87
+ isActive: { [FilterOperand.eq]: 'true' }
88
+ }
56
89
  },
57
- include: ['addresses', 'comments', 'roles', 'manager'],
90
+ include: ['addresses', 'roles']
58
91
  });
59
92
 
93
+ // Get one user
94
+ const user = await jsonSdk.jonApiSdkService.getOne(Users, '1', {
95
+ include: ['addresses', 'comments', 'roles', 'manager']
96
+ });
60
97
 
61
- // Atomic Operation
62
-
63
- const address = new Addresses();
64
-
65
- address.city = faker.string.alpha(50);
66
- address.state = faker.string.alpha(50);
67
- address.country = faker.string.alpha(50);
68
- address.id = 10000;
69
-
70
- const manager = getUser();
71
- manager.id = 10001;
72
- manager.addresses = address;
98
+ // Create a user
99
+ const newUser = new Users();
100
+ newUser.firstName = 'John';
101
+ newUser.lastName = 'Doe';
102
+ newUser.login = 'johndoe';
103
+ newUser.isActive = true;
73
104
 
74
- const roles = new Roles();
75
- roles.id = 10002;
76
- roles.name = faker.string.alpha(50);
77
- roles.key = faker.string.alpha(50);
105
+ const createdUser = await jsonSdk.jonApiSdkService.postOne(newUser);
78
106
 
79
- const user = getUser();
80
- user.addresses = address;
81
- user.manager = manager;
82
- user.roles = [roles];
107
+ // Update a user
108
+ createdUser.firstName = 'Jane';
109
+ const updatedUser = await jsonSdk.jonApiSdkService.patchOne(createdUser);
83
110
 
84
- const [addressPost, managerPost, rolesPost, userPost] = await jsonSdk
85
- .atomicFactory()
86
- .postOne(address)
87
- .postOne(manager)
88
- .postOne(roles)
89
- .postOne(user)
90
- .run();
111
+ // Delete a user
112
+ await jsonSdk.jonApiSdkService.deleteOne(createdUser);
113
+ ```
91
114
 
115
+ ### Angular Setup
92
116
 
93
- ```
94
- or you can use Angular module:
95
117
  ```typescript
96
- import {
97
- provideJsonApi,
98
- AtomicFactory,
99
- JsonApiSdkService
118
+ import {
119
+ provideJsonApi,
120
+ AtomicFactory,
121
+ JsonApiSdkService
100
122
  } from '@klerick/json-api-nestjs-sdk/ngModule';
101
123
  import {
102
124
  provideHttpClient,
103
125
  withFetch,
104
126
  } from '@angular/common/http';
127
+ import { Component, inject } from '@angular/core';
128
+ import { bootstrapApplication } from '@angular/platform-browser';
105
129
 
106
- @Component({
107
- standalone: true,
108
- selector: 'nestjs-json-api-root',
109
- templateUrl: './app.component.html',
110
- styleUrl: './app.component.css',
111
- })
112
- export class AppComponent {
113
- private JsonApiSdkService = inject(JsonApiSdkService);
114
- private atomicFactory = inject(AtomicFactory);
115
- }
116
-
117
- const angularConfig: JsonSdkConfig = {
118
- apiHost: 'http://localhost:4200',
130
+ // 1. Configure in your main.ts or app.config.ts
131
+ const angularConfig = {
132
+ apiHost: 'http://localhost:3000',
119
133
  idKey: 'id',
120
134
  apiPrefix: 'api',
121
135
  operationUrl: 'operation',
122
- }
136
+ dateFields: ['createdAt', 'updatedAt']
137
+ };
123
138
 
124
139
  bootstrapApplication(AppComponent, {
125
140
  providers: [
126
141
  provideHttpClient(withFetch()),
127
142
  provideJsonApi(angularConfig)
128
143
  ],
129
- }).catch((err) =>
130
- console.error(err)
131
- );
132
-
144
+ }).catch((err) => console.error(err));
133
145
 
146
+ // 2. Use in your components
147
+ @Component({
148
+ standalone: true,
149
+ selector: 'app-users',
150
+ templateUrl: './users.component.html',
151
+ })
152
+ export class UsersComponent {
153
+ private jsonApiService = inject(JsonApiSdkService);
154
+ private atomicFactory = inject(AtomicFactory);
134
155
 
156
+ async loadUsers() {
157
+ const users = await this.jsonApiService.getAll(Users, {
158
+ include: ['addresses']
159
+ });
160
+ return users;
161
+ }
162
+
163
+ async createMultipleResources() {
164
+ const result = await this.atomicFactory()
165
+ .postOne(newUser)
166
+ .postOne(newAddress)
167
+ .run();
168
+ }
169
+ }
135
170
  ```
136
171
 
137
- ## Configuration params
172
+ ## ⚙️ Configuration
138
173
 
139
- ```typescript
174
+ ### JsonConfig Type
175
+
176
+ ```typescript
140
177
  type JsonSdkConfig = {
141
- apiHost: string; // url for api
142
- apiPrefix?: string; // prefex for api - api/v1/....
143
- idKey?: string; // name for id field
144
- idIsNumber?: boolean; // use parseInt for id field
145
- operationUrl?: string; // url for atomic operation
146
- dateFields: string[] // array of dateField for create date object - ;
147
- }
178
+ apiHost: string; // Base URL of your API (e.g., 'http://localhost:3000')
179
+ apiPrefix?: string; // API prefix (e.g., 'api' -> '/api/users')
180
+ idKey?: string; // Name of ID field (default: 'id')
181
+ idIsNumber?: boolean; // Parse IDs as numbers (default: false)
182
+ operationUrl?: string; // URL path for atomic operations (default: 'operation')
183
+ dateFields?: string[]; // Fields to convert to Date objects (e.g., ['createdAt', 'updatedAt'])
184
+ }
148
185
 
149
186
  type JsonConfig = JsonSdkConfig & {
150
- adapter?: HttpInnerClient; // by default use fetch for http request but you can change it
187
+ adapter?: HttpInnerClient; // HTTP client adapter (default: fetch)
188
+ }
189
+ ```
190
+
191
+ ### HTTP Adapters
192
+
193
+ **Axios Adapter:**
194
+ ```typescript
195
+ import { adapterForAxios } from '@klerick/json-api-nestjs-sdk';
196
+ import axios from 'axios';
197
+
198
+ const adapter = adapterForAxios(axios);
199
+ ```
200
+
201
+ **Fetch API (default):**
202
+ ```typescript
203
+ // No adapter needed, fetch is used by default
204
+ const jsonSdk = JsonApiJs({
205
+ apiHost: 'http://localhost:3000',
206
+ apiPrefix: 'api',
207
+ }, true);
208
+ ```
209
+
210
+ **Custom Adapter:**
211
+
212
+ See [HttpInnerClient interface](https://github.com/klerick/nestjs-json-api/blob/master/libs/json-api/json-api-nestjs-sdk/src/lib/types/http-inner-client.ts) for implementation details.
213
+
214
+ ---
215
+
216
+ ## 📖 API Methods
217
+
218
+ ### Fetching Resources
219
+
220
+ #### `getAll(Entity, options?)`
221
+
222
+ Fetch all resources with optional filtering, sorting, and relationships.
223
+
224
+ ```typescript
225
+ import { FilterOperand } from '@klerick/json-api-nestjs-sdk';
226
+
227
+ // Fetch all users
228
+ const users = await jsonSdk.jonApiSdkService.getAll(Users);
229
+
230
+ // With filtering
231
+ const activeUsers = await jsonSdk.jonApiSdkService.getAll(Users, {
232
+ filter: {
233
+ target: {
234
+ isActive: { [FilterOperand.eq]: 'true' },
235
+ id: { [FilterOperand.in]: ['1', '2', '3'] }
236
+ }
237
+ },
238
+ include: ['addresses', 'roles']
239
+ });
240
+
241
+ // Filter by relationship
242
+ const usersWithRoles = await jsonSdk.jonApiSdkService.getAll(Users, {
243
+ filter: {
244
+ target: {
245
+ id: { [FilterOperand.in]: ['1', '2'] }
246
+ },
247
+ roles: {
248
+ name: { [FilterOperand.eq]: 'admin' }
249
+ }
250
+ },
251
+ include: ['roles']
252
+ });
253
+ ```
254
+
255
+ #### `getList(Entity, options)`
256
+
257
+ Fetch resources with pagination (returns paginated results).
258
+
259
+ ```typescript
260
+ const firstPage = await jsonSdk.jonApiSdkService.getList(Users, {
261
+ page: {
262
+ number: 1,
263
+ size: 10
264
+ },
265
+ sort: {
266
+ target: {
267
+ id: 'ASC'
268
+ }
269
+ }
270
+ });
271
+
272
+ const secondPage = await jsonSdk.jonApiSdkService.getList(Users, {
273
+ page: {
274
+ number: 2,
275
+ size: 10
276
+ },
277
+ sort: {
278
+ target: {
279
+ createdAt: 'DESC'
280
+ }
281
+ }
282
+ });
283
+ ```
284
+
285
+ #### `getOne(Entity, id, options?)`
286
+
287
+ Fetch a single resource by ID.
288
+
289
+ ```typescript
290
+ // Simple fetch
291
+ const user = await jsonSdk.jonApiSdkService.getOne(Users, '1');
292
+
293
+ // With relationships
294
+ const userWithRelations = await jsonSdk.jonApiSdkService.getOne(Users, '1', {
295
+ include: ['addresses', 'comments', 'roles', 'manager']
296
+ });
297
+
298
+ // With sparse fieldsets
299
+ const userPartial = await jsonSdk.jonApiSdkService.getOne(Users, '1', {
300
+ fields: {
301
+ users: ['firstName', 'lastName', 'email']
302
+ }
303
+ });
304
+ ```
305
+
306
+ ### Creating Resources
307
+
308
+ #### `postOne(entity, options?)`
309
+
310
+ Create a new resource.
311
+
312
+ ```typescript
313
+ // Simple create
314
+ const newUser = new Users();
315
+ newUser.firstName = 'John';
316
+ newUser.lastName = 'Doe';
317
+ newUser.login = 'johndoe';
318
+ newUser.isActive = true;
319
+
320
+ const createdUser = await jsonSdk.jonApiSdkService.postOne(newUser);
321
+
322
+ // Create with relationships
323
+ const newAddress = new Addresses();
324
+ newAddress.city = 'New York';
325
+ newAddress.state = 'NY';
326
+ newAddress.country = 'USA';
327
+
328
+ const savedAddress = await jsonSdk.jonApiSdkService.postOne(newAddress);
329
+
330
+ const user = new Users();
331
+ user.firstName = 'Jane';
332
+ user.lastName = 'Doe';
333
+ user.login = 'janedoe';
334
+ user.addresses = savedAddress; // Set relationship
335
+
336
+ const createdUser = await jsonSdk.jonApiSdkService.postOne(user);
337
+ ```
338
+
339
+ ### Updating Resources
340
+
341
+ #### `patchOne(entity, options?)`
342
+
343
+ Update an existing resource.
344
+
345
+ ```typescript
346
+ // Update attributes
347
+ user.firstName = 'Updated Name';
348
+ const updatedUser = await jsonSdk.jonApiSdkService.patchOne(user);
349
+
350
+ // Update relationships
351
+ const newAddress = await jsonSdk.jonApiSdkService.postOne(addressEntity);
352
+ user.addresses = newAddress;
353
+
354
+ const updatedUser = await jsonSdk.jonApiSdkService.patchOne(user);
355
+ ```
356
+
357
+ ### Deleting Resources
358
+
359
+ #### `deleteOne(entity)`
360
+
361
+ Delete a resource.
362
+
363
+ ```typescript
364
+ await jsonSdk.jonApiSdkService.deleteOne(user);
365
+ ```
366
+
367
+ ### Relationship Operations
368
+
369
+ #### `deleteRelationships(entity, relationshipName)`
370
+
371
+ Remove relationships without deleting the related resources.
372
+
373
+ ```typescript
374
+ // Remove all roles from user
375
+ await jsonSdk.jonApiSdkService.deleteRelationships(user, 'roles');
376
+
377
+ // Remove manager from user
378
+ await jsonSdk.jonApiSdkService.deleteRelationships(user, 'manager');
379
+
380
+ // Remove all comments from user
381
+ await jsonSdk.jonApiSdkService.deleteRelationships(user, 'comments');
382
+ ```
383
+
384
+ ---
385
+
386
+ ## 🔍 Query Options
387
+
388
+ ### Filtering
389
+
390
+ Available operators:
391
+
392
+ ```typescript
393
+ enum FilterOperand {
394
+ eq = 'eq', // Equal
395
+ ne = 'ne', // Not equal
396
+ in = 'in', // In array
397
+ nin = 'nin', // Not in array
398
+ lt = 'lt', // Less than
399
+ lte = 'lte', // Less than or equal
400
+ gt = 'gt', // Greater than
401
+ gte = 'gte', // Greater than or equal
402
+ like = 'like', // SQL LIKE
403
+ re = 'regexp', // Regular expression
151
404
  }
152
405
  ```
153
- * You can see interface of [HttpInnerClient](https://github.com/klerick/nestjs-json-api/blob/master/libs/json-api/json-api-nestjs-sdk/src/lib/types/http-inner-client.ts)
154
- * More example you can see [here](https://github.com/klerick/nestjs-json-api/blob/master/apps/json-api-server-e2e/src/json-api/json-api-sdk) or [here](https://github.com/klerick/nestjs-json-api/blob/master/apps/json-api-front/src/app/app.component.ts)
406
+
407
+ **Examples:**
408
+
409
+ ```typescript
410
+ // Equal
411
+ const users = await jsonSdk.jonApiSdkService.getAll(Users, {
412
+ filter: {
413
+ target: {
414
+ isActive: { [FilterOperand.eq]: 'true' }
415
+ }
416
+ }
417
+ });
418
+
419
+ // Not equal
420
+ const inactiveUsers = await jsonSdk.jonApiSdkService.getAll(Users, {
421
+ filter: {
422
+ target: {
423
+ isActive: { [FilterOperand.ne]: 'true' }
424
+ }
425
+ }
426
+ });
427
+
428
+ // In array
429
+ const specificUsers = await jsonSdk.jonApiSdkService.getAll(Users, {
430
+ filter: {
431
+ target: {
432
+ id: { [FilterOperand.in]: ['1', '2', '3'] }
433
+ }
434
+ }
435
+ });
436
+
437
+ // LIKE search
438
+ const searchUsers = await jsonSdk.jonApiSdkService.getAll(Users, {
439
+ filter: {
440
+ target: {
441
+ login: { [FilterOperand.like]: 'john' }
442
+ }
443
+ }
444
+ });
445
+
446
+ // Check null/not null
447
+ const usersWithManager = await jsonSdk.jonApiSdkService.getAll(Users, {
448
+ filter: {
449
+ target: {
450
+ manager: { [FilterOperand.ne]: null }
451
+ }
452
+ }
453
+ });
454
+
455
+ const usersWithoutManager = await jsonSdk.jonApiSdkService.getAll(Users, {
456
+ filter: {
457
+ target: {
458
+ manager: { [FilterOperand.eq]: null }
459
+ }
460
+ }
461
+ });
462
+ ```
463
+
464
+ ### Sorting
465
+
466
+ ```typescript
467
+ // Sort by single field
468
+ const users = await jsonSdk.jonApiSdkService.getList(Users, {
469
+ sort: {
470
+ target: {
471
+ id: 'ASC'
472
+ }
473
+ }
474
+ });
475
+
476
+ // Sort by multiple fields
477
+ const sortedUsers = await jsonSdk.jonApiSdkService.getList(Users, {
478
+ sort: {
479
+ target: {
480
+ createdAt: 'DESC',
481
+ lastName: 'ASC'
482
+ }
483
+ }
484
+ });
485
+ ```
486
+
487
+ ### Pagination
488
+
489
+ ```typescript
490
+ const paginatedUsers = await jsonSdk.jonApiSdkService.getList(Users, {
491
+ page: {
492
+ number: 1, // Page number (1-indexed)
493
+ size: 20 // Items per page
494
+ }
495
+ });
496
+ ```
497
+
498
+ ### Including Relationships
499
+
500
+ ```typescript
501
+ // Include single relationship
502
+ const users = await jsonSdk.jonApiSdkService.getAll(Users, {
503
+ include: ['addresses']
504
+ });
505
+
506
+ // Include multiple relationships
507
+ const usersWithAll = await jsonSdk.jonApiSdkService.getAll(Users, {
508
+ include: ['addresses', 'roles', 'comments', 'manager']
509
+ });
510
+
511
+ // Include nested relationships
512
+ const usersWithNested = await jsonSdk.jonApiSdkService.getAll(Users, {
513
+ include: ['addresses', 'manager.addresses', 'roles']
514
+ });
515
+ ```
516
+
517
+ ### Sparse Fieldsets
518
+
519
+ Request only specific fields to reduce payload size.
520
+
521
+ ```typescript
522
+ const users = await jsonSdk.jonApiSdkService.getAll(Users, {
523
+ fields: {
524
+ users: ['firstName', 'lastName', 'email'],
525
+ addresses: ['city', 'country']
526
+ },
527
+ include: ['addresses']
528
+ });
529
+ ```
530
+
531
+ ---
532
+
533
+ ## ⚡ Atomic Operations
534
+
535
+ Execute multiple operations in a single HTTP request. All operations succeed or fail together.
536
+
537
+ ### Basic Atomic Operation
538
+
539
+ ```typescript
540
+ const newUser = new Users();
541
+ newUser.firstName = 'John';
542
+ newUser.lastName = 'Doe';
543
+ newUser.login = 'johndoe';
544
+
545
+ const result = await jsonSdk.atomicFactory()
546
+ .postOne(newUser)
547
+ .run();
548
+
549
+ console.log(result[0]); // Created user
550
+ ```
551
+
552
+ ### Multiple Operations
553
+
554
+ ```typescript
555
+ // Create multiple related resources
556
+ const address = new Addresses();
557
+ address.city = 'New York';
558
+ address.state = 'NY';
559
+ address.country = 'USA';
560
+
561
+ const role = new Roles();
562
+ role.name = 'Admin';
563
+ role.key = 'admin';
564
+
565
+ const user = new Users();
566
+ user.firstName = 'Jane';
567
+ user.lastName = 'Doe';
568
+ user.login = 'janedoe';
569
+ user.addresses = address;
570
+ user.roles = [role];
571
+
572
+ const [createdAddress, createdRole, createdUser] = await jsonSdk
573
+ .atomicFactory()
574
+ .postOne(address)
575
+ .postOne(role)
576
+ .postOne(user)
577
+ .run();
578
+ ```
579
+
580
+ ### Mixed Operations (POST, PATCH, Relationships)
581
+
582
+ ```typescript
583
+ // Create user first
584
+ const newUser = new Users();
585
+ newUser.firstName = 'John';
586
+ newUser.login = 'john';
587
+
588
+ const [createdUser] = await jsonSdk.atomicFactory()
589
+ .postOne(newUser)
590
+ .run();
591
+
592
+ // Then update and manage relationships atomically
593
+ const patchUser = Object.assign(new Users(), createdUser);
594
+ patchUser.firstName = 'John Updated';
595
+ patchUser.roles = [role1];
596
+
597
+ const patchUser2 = Object.assign(new Users(), createdUser);
598
+ patchUser2.comments = [comment1];
599
+
600
+ const patchUser3 = Object.assign(new Users(), createdUser);
601
+ patchUser3.comments = [comment2];
602
+
603
+ const result = await jsonSdk
604
+ .atomicFactory()
605
+ .patchOne(patchUser) // Update user attributes and set roles
606
+ .patchOne(patchUser2) // Set comments
607
+ .patchRelationships(patchUser2, 'comments') // Replace comments (keep only comment1)
608
+ .postRelationships(patchUser3, 'comments') // Add comment2 to existing comments
609
+ .run();
610
+
611
+ // result[0] - updated user
612
+ // result[1] - updated user with comments
613
+ // result[2] - array of comment IDs after replacement
614
+ // result[3] - array of all comment IDs after addition
615
+ ```
616
+
617
+ ### Using Temporary IDs (lid)
618
+
619
+ Reference resources created within the same atomic request using temporary IDs.
620
+
621
+ ```typescript
622
+ const address = new Addresses();
623
+ address.city = 'Boston';
624
+ address.id = 10000; // Temporary ID
625
+
626
+ const user = new Users();
627
+ user.firstName = 'Alice';
628
+ user.addresses = address; // Reference by temp ID
629
+
630
+ const [createdAddress, createdUser] = await jsonSdk
631
+ .atomicFactory()
632
+ .postOne(address)
633
+ .postOne(user)
634
+ .run();
635
+
636
+ // Server assigns real IDs
637
+ console.log(createdAddress.id); // Real ID (e.g., 1)
638
+ console.log(createdUser.addresses.id); // Same real ID
639
+ ```
640
+
641
+ ---
642
+
643
+ ## 💡 Examples
644
+
645
+ For comprehensive real-world examples, see the [E2E test suite](https://github.com/klerick/nestjs-json-api/tree/master/apps/json-api-server-e2e/src/json-api/json-api-sdk):
646
+
647
+ - **[GET Operations](https://github.com/klerick/nestjs-json-api/blob/master/apps/json-api-server-e2e/src/json-api/json-api-sdk/get-method.spec.ts)** - Fetching, filtering, pagination, sparse fieldsets
648
+ - **[POST Operations](https://github.com/klerick/nestjs-json-api/blob/master/apps/json-api-server-e2e/src/json-api/json-api-sdk/post-method.spec.ts)** - Creating resources with relationships
649
+ - **[PATCH Operations](https://github.com/klerick/nestjs-json-api/blob/master/apps/json-api-server-e2e/src/json-api/json-api-sdk/patch-methode.spec.ts)** - Updating resources and relationships
650
+ - **[Atomic Operations](https://github.com/klerick/nestjs-json-api/blob/master/apps/json-api-server-e2e/src/json-api/json-api-sdk/atomic-sdk.spec.ts)** - Batch requests with rollback
651
+ - **[Common Decorators](https://github.com/klerick/nestjs-json-api/blob/master/apps/json-api-server-e2e/src/json-api/json-api-sdk/check-common-decorator.spec.ts)** - Guards, interceptors, custom behavior
652
+
653
+ ---
654
+
655
+ ## 📝 License
656
+
657
+ MIT
658
+
659
+ ---
660
+
661
+ ## 🔗 Related Packages
662
+
663
+ - [@klerick/json-api-nestjs](https://www.npmjs.com/package/@klerick/json-api-nestjs) - JSON:API server implementation for NestJS
664
+ - [@klerick/json-api-nestjs-typeorm](https://www.npmjs.com/package/@klerick/json-api-nestjs-typeorm) - TypeORM adapter
665
+ - [@klerick/json-api-nestjs-microorm](https://www.npmjs.com/package/@klerick/json-api-nestjs-microorm) - MikroORM adapter