@ackplus/nest-crud-request 1.1.8 → 1.1.16
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/README.md +708 -6
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/lib/query-builder.d.ts.map +1 -0
- package/{src → dist}/lib/query-builder.js +9 -13
- package/dist/lib/relation-builder.d.ts.map +1 -0
- package/{src → dist}/lib/relation-builder.js +1 -5
- package/dist/lib/types.d.ts.map +1 -0
- package/{src → dist}/lib/types.js +6 -9
- package/dist/lib/utils.d.ts.map +1 -0
- package/{src → dist}/lib/utils.js +1 -4
- package/dist/lib/where-builder.d.ts.map +1 -0
- package/{src → dist}/lib/where-builder.js +6 -10
- package/package.json +39 -2
- package/src/index.d.ts.map +0 -1
- package/src/index.js +0 -6
- package/src/lib/query-builder.d.ts.map +0 -1
- package/src/lib/relation-builder.d.ts.map +0 -1
- package/src/lib/types.d.ts.map +0 -1
- package/src/lib/utils.d.ts.map +0 -1
- package/src/lib/where-builder.d.ts.map +0 -1
- /package/{src → dist}/index.d.ts +0 -0
- /package/{src → dist}/lib/query-builder.d.ts +0 -0
- /package/{src → dist}/lib/relation-builder.d.ts +0 -0
- /package/{src → dist}/lib/types.d.ts +0 -0
- /package/{src → dist}/lib/utils.d.ts +0 -0
- /package/{src → dist}/lib/where-builder.d.ts +0 -0
package/README.md
CHANGED
|
@@ -1,11 +1,713 @@
|
|
|
1
|
-
# nest-crud-request
|
|
1
|
+
# @ackplus/nest-crud-request
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Framework-agnostic query builder for REST APIs - build complex queries with filtering, relations, and pagination for both frontend and backend.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- 🎯 **Framework-agnostic** - Works with any JavaScript/TypeScript framework
|
|
8
|
+
- 🌐 **Frontend & Backend** - Use in React, Angular, Vue, Node.js, or any other environment
|
|
9
|
+
- 🔍 **Type-safe** - Full TypeScript support with comprehensive type definitions
|
|
10
|
+
- 🔗 **Fluent API** - Chainable methods for building complex queries
|
|
11
|
+
- 📦 **Zero dependencies** - Lightweight with minimal footprint
|
|
12
|
+
- 🎨 **Clean syntax** - Intuitive and readable query construction
|
|
13
|
+
- 🔄 **Flexible output** - Export as query string, JSON, or object
|
|
8
14
|
|
|
9
|
-
##
|
|
15
|
+
## Installation
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
```bash
|
|
18
|
+
npm install @ackplus/nest-crud-request
|
|
19
|
+
# or
|
|
20
|
+
pnpm add @ackplus/nest-crud-request
|
|
21
|
+
# or
|
|
22
|
+
yarn add @ackplus/nest-crud-request
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { QueryBuilder, WhereOperatorEnum, OrderDirectionEnum } from '@ackplus/nest-crud-request';
|
|
29
|
+
|
|
30
|
+
// Create a query builder
|
|
31
|
+
const query = new QueryBuilder()
|
|
32
|
+
.where('isActive', WhereOperatorEnum.EQ, true)
|
|
33
|
+
.andWhere('role', WhereOperatorEnum.IN, ['admin', 'moderator'])
|
|
34
|
+
.addRelation('posts', ['id', 'title'])
|
|
35
|
+
.setSkip(0)
|
|
36
|
+
.setTake(10)
|
|
37
|
+
.addOrder('createdAt', OrderDirectionEnum.DESC);
|
|
38
|
+
|
|
39
|
+
// Convert to query string parameters
|
|
40
|
+
const params = query.toObject();
|
|
41
|
+
// {
|
|
42
|
+
// where: '{"isActive":{"$eq":true},"$and":[{"role":{"$in":["admin","moderator"]}}]}',
|
|
43
|
+
// relations: '{"posts":{"select":["id","title"]}}',
|
|
44
|
+
// skip: 0,
|
|
45
|
+
// take: 10,
|
|
46
|
+
// order: '{"createdAt":"DESC"}'
|
|
47
|
+
// }
|
|
48
|
+
|
|
49
|
+
// Use with fetch or axios
|
|
50
|
+
const response = await fetch(`/api/users?${new URLSearchParams(params)}`);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Usage Examples
|
|
54
|
+
|
|
55
|
+
### Frontend Usage (React)
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { QueryBuilder, WhereOperatorEnum, OrderDirectionEnum } from '@ackplus/nest-crud-request';
|
|
59
|
+
import { useState, useEffect } from 'react';
|
|
60
|
+
|
|
61
|
+
function UserList() {
|
|
62
|
+
const [users, setUsers] = useState([]);
|
|
63
|
+
const [page, setPage] = useState(0);
|
|
64
|
+
const pageSize = 10;
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
const fetchUsers = async () => {
|
|
68
|
+
const query = new QueryBuilder()
|
|
69
|
+
.where('isActive', WhereOperatorEnum.EQ, true)
|
|
70
|
+
.addRelation('posts')
|
|
71
|
+
.setSkip(page * pageSize)
|
|
72
|
+
.setTake(pageSize)
|
|
73
|
+
.addOrder('createdAt', OrderDirectionEnum.DESC);
|
|
74
|
+
|
|
75
|
+
const params = query.toObject();
|
|
76
|
+
const queryString = new URLSearchParams(params).toString();
|
|
77
|
+
|
|
78
|
+
const response = await fetch(`/api/users?${queryString}`);
|
|
79
|
+
const data = await response.json();
|
|
80
|
+
setUsers(data);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
fetchUsers();
|
|
84
|
+
}, [page]);
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<div>
|
|
88
|
+
{users.map(user => (
|
|
89
|
+
<div key={user.id}>{user.email}</div>
|
|
90
|
+
))}
|
|
91
|
+
<button onClick={() => setPage(page - 1)} disabled={page === 0}>
|
|
92
|
+
Previous
|
|
93
|
+
</button>
|
|
94
|
+
<button onClick={() => setPage(page + 1)}>
|
|
95
|
+
Next
|
|
96
|
+
</button>
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Frontend Usage (Angular)
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { QueryBuilder, WhereOperatorEnum, OrderDirectionEnum } from '@ackplus/nest-crud-request';
|
|
106
|
+
import { HttpClient } from '@angular/common/http';
|
|
107
|
+
import { Injectable } from '@angular/core';
|
|
108
|
+
import { Observable } from 'rxjs';
|
|
109
|
+
|
|
110
|
+
@Injectable()
|
|
111
|
+
export class UserService {
|
|
112
|
+
constructor(private http: HttpClient) {}
|
|
113
|
+
|
|
114
|
+
getUsers(page: number = 0, pageSize: number = 10): Observable<User[]> {
|
|
115
|
+
const query = new QueryBuilder()
|
|
116
|
+
.where('isActive', WhereOperatorEnum.EQ, true)
|
|
117
|
+
.addRelation('posts')
|
|
118
|
+
.setSkip(page * pageSize)
|
|
119
|
+
.setTake(pageSize)
|
|
120
|
+
.addOrder('createdAt', OrderDirectionEnum.DESC);
|
|
121
|
+
|
|
122
|
+
const params = query.toObject();
|
|
123
|
+
|
|
124
|
+
return this.http.get<User[]>('/api/users', { params });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
searchUsers(searchTerm: string): Observable<User[]> {
|
|
128
|
+
const query = new QueryBuilder()
|
|
129
|
+
.where((builder) => {
|
|
130
|
+
builder
|
|
131
|
+
.where('email', WhereOperatorEnum.ILIKE, `%${searchTerm}%`)
|
|
132
|
+
.orWhere('firstName', WhereOperatorEnum.ILIKE, `%${searchTerm}%`)
|
|
133
|
+
.orWhere('lastName', WhereOperatorEnum.ILIKE, `%${searchTerm}%`);
|
|
134
|
+
})
|
|
135
|
+
.addOrder('email', OrderDirectionEnum.ASC);
|
|
136
|
+
|
|
137
|
+
const params = query.toObject();
|
|
138
|
+
|
|
139
|
+
return this.http.get<User[]>('/api/users', { params });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Frontend Usage (Vue 3)
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { QueryBuilder, WhereOperatorEnum, OrderDirectionEnum } from '@ackplus/nest-crud-request';
|
|
148
|
+
import { ref, onMounted } from 'vue';
|
|
149
|
+
import axios from 'axios';
|
|
150
|
+
|
|
151
|
+
export function useUsers() {
|
|
152
|
+
const users = ref([]);
|
|
153
|
+
const loading = ref(false);
|
|
154
|
+
const page = ref(0);
|
|
155
|
+
const pageSize = 10;
|
|
156
|
+
|
|
157
|
+
const fetchUsers = async () => {
|
|
158
|
+
loading.value = true;
|
|
159
|
+
|
|
160
|
+
const query = new QueryBuilder()
|
|
161
|
+
.where('isActive', WhereOperatorEnum.EQ, true)
|
|
162
|
+
.addRelation('posts')
|
|
163
|
+
.setSkip(page.value * pageSize)
|
|
164
|
+
.setTake(pageSize)
|
|
165
|
+
.addOrder('createdAt', OrderDirectionEnum.DESC);
|
|
166
|
+
|
|
167
|
+
const params = query.toObject();
|
|
168
|
+
|
|
169
|
+
const response = await axios.get('/api/users', { params });
|
|
170
|
+
users.value = response.data;
|
|
171
|
+
loading.value = false;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const searchUsers = async (searchTerm: string) => {
|
|
175
|
+
const query = new QueryBuilder()
|
|
176
|
+
.where((builder) => {
|
|
177
|
+
builder
|
|
178
|
+
.where('email', WhereOperatorEnum.ILIKE, `%${searchTerm}%`)
|
|
179
|
+
.orWhere('firstName', WhereOperatorEnum.ILIKE, `%${searchTerm}%`);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const params = query.toObject();
|
|
183
|
+
const response = await axios.get('/api/users', { params });
|
|
184
|
+
users.value = response.data;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
onMounted(fetchUsers);
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
users,
|
|
191
|
+
loading,
|
|
192
|
+
page,
|
|
193
|
+
fetchUsers,
|
|
194
|
+
searchUsers,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Backend Usage (Node.js/Express)
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import { QueryBuilder, WhereOperatorEnum } from '@ackplus/nest-crud-request';
|
|
203
|
+
|
|
204
|
+
// Parse query from request
|
|
205
|
+
app.get('/api/users', (req, res) => {
|
|
206
|
+
// Build query from request params
|
|
207
|
+
const query = new QueryBuilder({
|
|
208
|
+
where: req.query.where,
|
|
209
|
+
relations: req.query.relations,
|
|
210
|
+
select: req.query.select,
|
|
211
|
+
skip: req.query.skip ? parseInt(req.query.skip) : 0,
|
|
212
|
+
take: req.query.take ? parseInt(req.query.take) : 10,
|
|
213
|
+
order: req.query.order,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Use the query to fetch data from database
|
|
217
|
+
// ... your database logic
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## API Reference
|
|
222
|
+
|
|
223
|
+
### QueryBuilder
|
|
224
|
+
|
|
225
|
+
#### Constructor
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
new QueryBuilder(options?: QueryBuilderOptions)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
#### Methods
|
|
232
|
+
|
|
233
|
+
##### `setOptions(options: QueryBuilderOptions): this`
|
|
234
|
+
|
|
235
|
+
Set all options at once.
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
query.setOptions({
|
|
239
|
+
where: { isActive: { $eq: true } },
|
|
240
|
+
relations: ['posts'],
|
|
241
|
+
skip: 0,
|
|
242
|
+
take: 10,
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
##### `mergeOptions(options: QueryBuilderOptions, deep?: boolean): this`
|
|
247
|
+
|
|
248
|
+
Merge options with existing options.
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
query.mergeOptions({
|
|
252
|
+
skip: 10,
|
|
253
|
+
take: 20,
|
|
254
|
+
});
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
##### `addSelect(fields: string | string[]): this`
|
|
258
|
+
|
|
259
|
+
Add fields to select.
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
query.addSelect(['id', 'email', 'firstName']);
|
|
263
|
+
query.addSelect('lastName');
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
##### `removeSelect(fields: string | string[]): this`
|
|
267
|
+
|
|
268
|
+
Remove fields from select.
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
query.removeSelect(['password', 'secretKey']);
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
##### `addRelation(relation: string, select?: string[], where?: Record<string, any>): this`
|
|
275
|
+
|
|
276
|
+
Add a relation to load.
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
// Simple relation
|
|
280
|
+
query.addRelation('posts');
|
|
281
|
+
|
|
282
|
+
// Relation with specific fields
|
|
283
|
+
query.addRelation('posts', ['id', 'title', 'content']);
|
|
284
|
+
|
|
285
|
+
// Relation with filter
|
|
286
|
+
query.addRelation('posts', ['id', 'title'], {
|
|
287
|
+
published: { $eq: true }
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
##### `removeRelation(relation: string): this`
|
|
292
|
+
|
|
293
|
+
Remove a relation.
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
query.removeRelation('posts');
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
##### `where(...args: WhereBuilderCondition): this`
|
|
300
|
+
|
|
301
|
+
Set where condition (replaces existing).
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
// Simple equality
|
|
305
|
+
query.where('email', 'john@example.com');
|
|
306
|
+
|
|
307
|
+
// With operator
|
|
308
|
+
query.where('age', WhereOperatorEnum.GT, 18);
|
|
309
|
+
|
|
310
|
+
// Object syntax
|
|
311
|
+
query.where({ isActive: { $eq: true } });
|
|
312
|
+
|
|
313
|
+
// Function syntax for complex conditions
|
|
314
|
+
query.where((builder) => {
|
|
315
|
+
builder
|
|
316
|
+
.where('role', WhereOperatorEnum.EQ, 'admin')
|
|
317
|
+
.orWhere('role', WhereOperatorEnum.EQ, 'moderator');
|
|
318
|
+
});
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
##### `andWhere(...args: WhereBuilderCondition): this`
|
|
322
|
+
|
|
323
|
+
Add an AND where condition.
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
query.where('isActive', WhereOperatorEnum.EQ, true)
|
|
327
|
+
.andWhere('role', WhereOperatorEnum.EQ, 'admin');
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
##### `orWhere(...args: WhereBuilderCondition): this`
|
|
331
|
+
|
|
332
|
+
Add an OR where condition.
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
query.where('role', WhereOperatorEnum.EQ, 'admin')
|
|
336
|
+
.orWhere('role', WhereOperatorEnum.EQ, 'moderator');
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
##### `addOrder(orderBy: string, order: OrderDirectionEnum): this`
|
|
340
|
+
|
|
341
|
+
Add an order clause.
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
query.addOrder('createdAt', OrderDirectionEnum.DESC);
|
|
345
|
+
query.addOrder('email', OrderDirectionEnum.ASC);
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
##### `removeOrder(orderBy: string): this`
|
|
349
|
+
|
|
350
|
+
Remove an order clause.
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
query.removeOrder('createdAt');
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
##### `setSkip(skip: number): this`
|
|
357
|
+
|
|
358
|
+
Set the skip (offset) value.
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
query.setSkip(10);
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
##### `setTake(take: number): this`
|
|
365
|
+
|
|
366
|
+
Set the take (limit) value.
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
query.setTake(10);
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
##### `setWithDeleted(withDeleted: boolean): this`
|
|
373
|
+
|
|
374
|
+
Include soft-deleted records.
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
query.setWithDeleted(true);
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
##### `setOnlyDeleted(onlyDeleted: boolean): this`
|
|
381
|
+
|
|
382
|
+
Only show soft-deleted records.
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
query.setOnlyDeleted(true);
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
##### `set(key: string, value: any): this`
|
|
389
|
+
|
|
390
|
+
Set a custom option.
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
query.set('customField', 'customValue');
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
##### `toObject(constrainToNestedObject?: boolean): QueryBuilderOptions`
|
|
397
|
+
|
|
398
|
+
Convert to object (suitable for query parameters).
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
const params = query.toObject();
|
|
402
|
+
// By default, nested objects are converted to JSON strings
|
|
403
|
+
// {
|
|
404
|
+
// where: '{"isActive":{"$eq":true}}',
|
|
405
|
+
// relations: '["posts"]',
|
|
406
|
+
// skip: 0,
|
|
407
|
+
// take: 10
|
|
408
|
+
// }
|
|
409
|
+
|
|
410
|
+
// Keep nested objects as objects
|
|
411
|
+
const paramsNested = query.toObject(true);
|
|
412
|
+
// {
|
|
413
|
+
// where: { isActive: { $eq: true } },
|
|
414
|
+
// relations: ['posts'],
|
|
415
|
+
// skip: 0,
|
|
416
|
+
// take: 10
|
|
417
|
+
// }
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
##### `toJson(): string`
|
|
421
|
+
|
|
422
|
+
Convert to JSON string.
|
|
423
|
+
|
|
424
|
+
```typescript
|
|
425
|
+
const json = query.toJson();
|
|
426
|
+
// '{"where":{"isActive":{"$eq":true}},"relations":["posts"],"skip":0,"take":10}'
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### WhereBuilder
|
|
430
|
+
|
|
431
|
+
The WhereBuilder is used internally by QueryBuilder but can also be used standalone.
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
import { WhereBuilder, WhereOperatorEnum } from '@ackplus/nest-crud-request';
|
|
435
|
+
|
|
436
|
+
const whereBuilder = new WhereBuilder()
|
|
437
|
+
.where('isActive', WhereOperatorEnum.EQ, true)
|
|
438
|
+
.andWhere('role', WhereOperatorEnum.IN, ['admin', 'moderator']);
|
|
439
|
+
|
|
440
|
+
const whereObject = whereBuilder.toObject();
|
|
441
|
+
const whereJson = whereBuilder.toJson();
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### Operators
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
enum WhereOperatorEnum {
|
|
448
|
+
EQ = '$eq', // Equal
|
|
449
|
+
NOT_EQ = '$ne', // Not equal
|
|
450
|
+
GT = '$gt', // Greater than
|
|
451
|
+
GT_OR_EQ = '$gte', // Greater than or equal
|
|
452
|
+
LT = '$lt', // Less than
|
|
453
|
+
LT_OR_EQ = '$lte', // Less than or equal
|
|
454
|
+
IN = '$in', // In array
|
|
455
|
+
NOT_IN = '$notIn', // Not in array
|
|
456
|
+
LIKE = '$like', // Like (case-sensitive)
|
|
457
|
+
NOT_LIKE = '$notLike', // Not like
|
|
458
|
+
ILIKE = '$iLike', // Like (case-insensitive)
|
|
459
|
+
NOT_ILIKE = '$notIlike', // Not like (case-insensitive)
|
|
460
|
+
IS_NULL = '$isNull', // Is null
|
|
461
|
+
IS_NOT_NULL = '$isNotNull', // Is not null
|
|
462
|
+
BETWEEN = '$between', // Between
|
|
463
|
+
NOT_BETWEEN = '$notBetween', // Not between
|
|
464
|
+
IS_TRUE = '$isTrue', // Is true
|
|
465
|
+
IS_FALSE = '$isFalse', // Is false
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### Order Direction
|
|
470
|
+
|
|
471
|
+
```typescript
|
|
472
|
+
enum OrderDirectionEnum {
|
|
473
|
+
ASC = 'ASC',
|
|
474
|
+
DESC = 'DESC',
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
## Advanced Examples
|
|
479
|
+
|
|
480
|
+
### Complex Filtering
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
const query = new QueryBuilder()
|
|
484
|
+
.where((builder) => {
|
|
485
|
+
builder
|
|
486
|
+
.where('isActive', WhereOperatorEnum.EQ, true)
|
|
487
|
+
.andWhere((subBuilder) => {
|
|
488
|
+
subBuilder
|
|
489
|
+
.where('role', WhereOperatorEnum.EQ, 'admin')
|
|
490
|
+
.orWhere('role', WhereOperatorEnum.EQ, 'moderator');
|
|
491
|
+
})
|
|
492
|
+
.andWhere('age', WhereOperatorEnum.BETWEEN, [18, 65]);
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
// Results in:
|
|
496
|
+
// {
|
|
497
|
+
// isActive: { $eq: true },
|
|
498
|
+
// $and: [
|
|
499
|
+
// {
|
|
500
|
+
// $or: [
|
|
501
|
+
// { role: { $eq: 'admin' } },
|
|
502
|
+
// { role: { $eq: 'moderator' } }
|
|
503
|
+
// ]
|
|
504
|
+
// },
|
|
505
|
+
// { age: { $between: [18, 65] } }
|
|
506
|
+
// ]
|
|
507
|
+
// }
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### Search Functionality
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
function buildSearchQuery(searchTerm: string) {
|
|
514
|
+
return new QueryBuilder()
|
|
515
|
+
.where((builder) => {
|
|
516
|
+
builder
|
|
517
|
+
.where('email', WhereOperatorEnum.ILIKE, `%${searchTerm}%`)
|
|
518
|
+
.orWhere('firstName', WhereOperatorEnum.ILIKE, `%${searchTerm}%`)
|
|
519
|
+
.orWhere('lastName', WhereOperatorEnum.ILIKE, `%${searchTerm}%`);
|
|
520
|
+
})
|
|
521
|
+
.addOrder('email', OrderDirectionEnum.ASC);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Usage
|
|
525
|
+
const searchQuery = buildSearchQuery('john');
|
|
526
|
+
const params = searchQuery.toObject();
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### Pagination Helper
|
|
530
|
+
|
|
531
|
+
```typescript
|
|
532
|
+
function buildPaginatedQuery(page: number, pageSize: number = 10) {
|
|
533
|
+
return new QueryBuilder()
|
|
534
|
+
.setSkip(page * pageSize)
|
|
535
|
+
.setTake(pageSize);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Usage
|
|
539
|
+
const paginatedQuery = buildPaginatedQuery(2, 20); // Page 3, 20 items per page
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Reusable Query Builder
|
|
543
|
+
|
|
544
|
+
```typescript
|
|
545
|
+
class UserQueryBuilder {
|
|
546
|
+
private builder: QueryBuilder;
|
|
547
|
+
|
|
548
|
+
constructor() {
|
|
549
|
+
this.builder = new QueryBuilder();
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
activeUsers() {
|
|
553
|
+
this.builder.andWhere('isActive', WhereOperatorEnum.EQ, true);
|
|
554
|
+
return this;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
admins() {
|
|
558
|
+
this.builder.andWhere('role', WhereOperatorEnum.EQ, 'admin');
|
|
559
|
+
return this;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
withPosts() {
|
|
563
|
+
this.builder.addRelation('posts', ['id', 'title', 'createdAt']);
|
|
564
|
+
return this;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
paginate(page: number, pageSize: number = 10) {
|
|
568
|
+
this.builder.setSkip(page * pageSize).setTake(pageSize);
|
|
569
|
+
return this;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
orderByNewest() {
|
|
573
|
+
this.builder.addOrder('createdAt', OrderDirectionEnum.DESC);
|
|
574
|
+
return this;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
build() {
|
|
578
|
+
return this.builder.toObject();
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Usage
|
|
583
|
+
const params = new UserQueryBuilder()
|
|
584
|
+
.activeUsers()
|
|
585
|
+
.admins()
|
|
586
|
+
.withPosts()
|
|
587
|
+
.paginate(0, 10)
|
|
588
|
+
.orderByNewest()
|
|
589
|
+
.build();
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
### Dynamic Filters
|
|
593
|
+
|
|
594
|
+
```typescript
|
|
595
|
+
interface FilterOptions {
|
|
596
|
+
search?: string;
|
|
597
|
+
role?: string;
|
|
598
|
+
isActive?: boolean;
|
|
599
|
+
minAge?: number;
|
|
600
|
+
maxAge?: number;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function buildDynamicQuery(filters: FilterOptions) {
|
|
604
|
+
const query = new QueryBuilder();
|
|
605
|
+
|
|
606
|
+
if (filters.search) {
|
|
607
|
+
query.where((builder) => {
|
|
608
|
+
builder
|
|
609
|
+
.where('email', WhereOperatorEnum.ILIKE, `%${filters.search}%`)
|
|
610
|
+
.orWhere('firstName', WhereOperatorEnum.ILIKE, `%${filters.search}%`)
|
|
611
|
+
.orWhere('lastName', WhereOperatorEnum.ILIKE, `%${filters.search}%`);
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (filters.role) {
|
|
616
|
+
query.andWhere('role', WhereOperatorEnum.EQ, filters.role);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
if (filters.isActive !== undefined) {
|
|
620
|
+
query.andWhere('isActive', WhereOperatorEnum.EQ, filters.isActive);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (filters.minAge !== undefined && filters.maxAge !== undefined) {
|
|
624
|
+
query.andWhere('age', WhereOperatorEnum.BETWEEN, [filters.minAge, filters.maxAge]);
|
|
625
|
+
} else if (filters.minAge !== undefined) {
|
|
626
|
+
query.andWhere('age', WhereOperatorEnum.GT_OR_EQ, filters.minAge);
|
|
627
|
+
} else if (filters.maxAge !== undefined) {
|
|
628
|
+
query.andWhere('age', WhereOperatorEnum.LT_OR_EQ, filters.maxAge);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
return query;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Usage
|
|
635
|
+
const params = buildDynamicQuery({
|
|
636
|
+
search: 'john',
|
|
637
|
+
role: 'admin',
|
|
638
|
+
isActive: true,
|
|
639
|
+
minAge: 18,
|
|
640
|
+
maxAge: 65,
|
|
641
|
+
}).toObject();
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
## TypeScript Support
|
|
645
|
+
|
|
646
|
+
Full TypeScript support with type definitions:
|
|
647
|
+
|
|
648
|
+
```typescript
|
|
649
|
+
import {
|
|
650
|
+
QueryBuilder,
|
|
651
|
+
QueryBuilderOptions,
|
|
652
|
+
WhereOperatorEnum,
|
|
653
|
+
OrderDirectionEnum,
|
|
654
|
+
WhereBuilder,
|
|
655
|
+
WhereOptions,
|
|
656
|
+
RelationOptions,
|
|
657
|
+
} from '@ackplus/nest-crud-request';
|
|
658
|
+
|
|
659
|
+
// Type-safe query building
|
|
660
|
+
const options: QueryBuilderOptions = {
|
|
661
|
+
where: { isActive: { $eq: true } },
|
|
662
|
+
relations: ['posts'],
|
|
663
|
+
select: ['id', 'email'],
|
|
664
|
+
skip: 0,
|
|
665
|
+
take: 10,
|
|
666
|
+
order: { createdAt: 'DESC' },
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
const query = new QueryBuilder(options);
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
## Best Practices
|
|
673
|
+
|
|
674
|
+
1. **Validate user input** - Always sanitize and validate search terms and filters from user input
|
|
675
|
+
2. **Set reasonable limits** - Use `setTake()` with reasonable defaults to prevent large data transfers
|
|
676
|
+
3. **Use select fields** - Only select fields you need to reduce payload size
|
|
677
|
+
4. **Reuse query builders** - Create reusable query builder classes for common patterns
|
|
678
|
+
5. **Type your responses** - Use TypeScript interfaces for API responses
|
|
679
|
+
6. **Handle errors** - Always handle network errors and invalid responses
|
|
680
|
+
7. **Cache when possible** - Cache query results when data doesn't change frequently
|
|
681
|
+
8. **Optimize relations** - Only load relations you actually need
|
|
682
|
+
|
|
683
|
+
## Framework-Specific Tips
|
|
684
|
+
|
|
685
|
+
### React
|
|
686
|
+
|
|
687
|
+
- Use `useMemo` to memoize query builders
|
|
688
|
+
- Create custom hooks for common queries
|
|
689
|
+
- Combine with React Query or SWR for caching
|
|
690
|
+
|
|
691
|
+
### Angular
|
|
692
|
+
|
|
693
|
+
- Create service classes for query building
|
|
694
|
+
- Use Angular's HttpClient for automatic JSON parsing
|
|
695
|
+
- Leverage RxJS operators for query composition
|
|
696
|
+
|
|
697
|
+
### Vue
|
|
698
|
+
|
|
699
|
+
- Use composables for query logic
|
|
700
|
+
- Leverage Vue's reactivity for dynamic queries
|
|
701
|
+
- Combine with Pinia for state management
|
|
702
|
+
|
|
703
|
+
## License
|
|
704
|
+
|
|
705
|
+
MIT
|
|
706
|
+
|
|
707
|
+
## Contributing
|
|
708
|
+
|
|
709
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
710
|
+
|
|
711
|
+
## Related Packages
|
|
712
|
+
|
|
713
|
+
- [@ackplus/nest-crud](../nest-crud) - NestJS CRUD operations with TypeORM
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-builder.d.ts","sourceRoot":"","sources":["../../src/lib/query-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElE,OAAO,EAAgB,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAGtE,qBAAa,YAAY;IAErB,OAAO,CAAC,OAAO,CAA2B;IAE1C,OAAO,CAAC,YAAY,CAAoC;IAExD,OAAO,CAAC,eAAe,CAA0C;gBAErD,OAAO,CAAC,EAAE,mBAAmB;IAMzC,UAAU,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI;IAO9C,YAAY,CAAC,OAAO,EAAE,mBAAmB,EAAE,IAAI,UAAQ,GAAG,IAAI;IAc9D,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IAa1C,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IAY7C,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAKnF,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKtC,KAAK,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK3C,QAAQ,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK9C,OAAO,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK7C,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,IAAI;IAQ1D,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAOlC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAK3B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAK3B,cAAc,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI;IAK1C,cAAc,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI;IAK1C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAKlC,QAAQ,CAAC,uBAAuB,UAAQ;;;;;;;;;;;IAoDxC,MAAM;CAKT"}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const utils_1 = require("./utils");
|
|
6
|
-
const where_builder_1 = require("./where-builder");
|
|
7
|
-
class QueryBuilder {
|
|
1
|
+
import { RelationBuilder } from './relation-builder';
|
|
2
|
+
import { deepMerge } from './utils';
|
|
3
|
+
import { WhereBuilder } from './where-builder';
|
|
4
|
+
export class QueryBuilder {
|
|
8
5
|
options = {};
|
|
9
|
-
whereBuilder = new
|
|
10
|
-
relationBuilder = new
|
|
6
|
+
whereBuilder = new WhereBuilder();
|
|
7
|
+
relationBuilder = new RelationBuilder();
|
|
11
8
|
constructor(options) {
|
|
12
9
|
if (options) {
|
|
13
10
|
this.setOptions(options);
|
|
@@ -15,14 +12,14 @@ class QueryBuilder {
|
|
|
15
12
|
}
|
|
16
13
|
setOptions(options) {
|
|
17
14
|
this.options = options;
|
|
18
|
-
this.whereBuilder = new
|
|
19
|
-
this.relationBuilder = new
|
|
15
|
+
this.whereBuilder = new WhereBuilder(options.where);
|
|
16
|
+
this.relationBuilder = new RelationBuilder(options.relations);
|
|
20
17
|
return this;
|
|
21
18
|
}
|
|
22
19
|
mergeOptions(options, deep = false) {
|
|
23
20
|
let updatedOptions = {};
|
|
24
21
|
if (deep) {
|
|
25
|
-
updatedOptions =
|
|
22
|
+
updatedOptions = deepMerge(this.options, options);
|
|
26
23
|
}
|
|
27
24
|
else {
|
|
28
25
|
updatedOptions = {
|
|
@@ -170,4 +167,3 @@ class QueryBuilder {
|
|
|
170
167
|
return JSON.stringify(obj);
|
|
171
168
|
}
|
|
172
169
|
}
|
|
173
|
-
exports.QueryBuilder = QueryBuilder;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relation-builder.d.ts","sourceRoot":"","sources":["../../src/lib/relation-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAuB,eAAe,EAAE,MAAM,SAAS,CAAC;AAG/E,qBAAa,eAAe;IAExB,OAAO,CAAC,SAAS,CAAsB;gBAE3B,SAAS,CAAC,EAAE,eAAe,GAAG,MAAM;IAOhD,YAAY,CAAC,SAAS,EAAE,eAAe,GAAG,IAAI;IAe9C,KAAK,IAAI,IAAI;IAKb,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAgB3E,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK9B,YAAY,IAAI,OAAO;IAIvB,QAAQ,IAAI,cAAc;IAI1B,MAAM,IAAI,MAAM;CAInB"}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RelationBuilder = void 0;
|
|
4
|
-
class RelationBuilder {
|
|
1
|
+
export class RelationBuilder {
|
|
5
2
|
relations = {};
|
|
6
3
|
constructor(relations) {
|
|
7
4
|
if (relations) {
|
|
@@ -59,4 +56,3 @@ class RelationBuilder {
|
|
|
59
56
|
return JSON.stringify(this.relations);
|
|
60
57
|
}
|
|
61
58
|
}
|
|
62
|
-
exports.RelationBuilder = RelationBuilder;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,eAAe,GAAG,MAAM,CAAC;IACrC,KAAK,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,MAAM,CAAC;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,oBAAY,wBAAwB;IAChC,GAAG,SAAS;IACZ,EAAE,QAAQ;CACb;AAED,oBAAY,iBAAiB;IACzB,EAAE,QAAQ;IACV,MAAM,QAAQ;IACd,EAAE,QAAQ;IACV,QAAQ,SAAS;IACjB,EAAE,QAAQ;IACV,QAAQ,SAAS;IACjB,EAAE,QAAQ;IACV,MAAM,WAAW;IACjB,IAAI,UAAU;IACd,QAAQ,aAAa;IACrB,KAAK,WAAW;IAChB,SAAS,cAAc;IACvB,OAAO,YAAY;IACnB,WAAW,eAAe;IAC1B,OAAO,aAAa;IACpB,WAAW,gBAAgB;IAC3B,OAAO,YAAY;IACnB,QAAQ,aAAa;CACxB;AAED,oBAAY,kBAAkB;IAC1B,GAAG,QAAQ;IACX,IAAI,SAAS;CAChB;AAED,MAAM,MAAM,WAAW,GAAG;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,IAAI,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE,CAAC;IACnC,GAAG,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,WAAW,EAAE,CAAC;AAGvD,MAAM,MAAM,mBAAmB,GAAG;IAC9B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,CAAC;AAE3E,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,cAAc,CAAC"}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.OrderDirectionEnum = exports.WhereOperatorEnum = exports.WhereLogicalOperatorEnum = void 0;
|
|
4
|
-
var WhereLogicalOperatorEnum;
|
|
1
|
+
export var WhereLogicalOperatorEnum;
|
|
5
2
|
(function (WhereLogicalOperatorEnum) {
|
|
6
3
|
WhereLogicalOperatorEnum["AND"] = "$and";
|
|
7
4
|
WhereLogicalOperatorEnum["OR"] = "$or";
|
|
8
|
-
})(WhereLogicalOperatorEnum || (
|
|
9
|
-
var WhereOperatorEnum;
|
|
5
|
+
})(WhereLogicalOperatorEnum || (WhereLogicalOperatorEnum = {}));
|
|
6
|
+
export var WhereOperatorEnum;
|
|
10
7
|
(function (WhereOperatorEnum) {
|
|
11
8
|
WhereOperatorEnum["EQ"] = "$eq";
|
|
12
9
|
WhereOperatorEnum["NOT_EQ"] = "$ne";
|
|
@@ -26,9 +23,9 @@ var WhereOperatorEnum;
|
|
|
26
23
|
WhereOperatorEnum["NOT_BETWEEN"] = "$notBetween";
|
|
27
24
|
WhereOperatorEnum["IS_TRUE"] = "$isTrue";
|
|
28
25
|
WhereOperatorEnum["IS_FALSE"] = "$isFalse";
|
|
29
|
-
})(WhereOperatorEnum || (
|
|
30
|
-
var OrderDirectionEnum;
|
|
26
|
+
})(WhereOperatorEnum || (WhereOperatorEnum = {}));
|
|
27
|
+
export var OrderDirectionEnum;
|
|
31
28
|
(function (OrderDirectionEnum) {
|
|
32
29
|
OrderDirectionEnum["ASC"] = "ASC";
|
|
33
30
|
OrderDirectionEnum["DESC"] = "DESC";
|
|
34
|
-
})(OrderDirectionEnum || (
|
|
31
|
+
})(OrderDirectionEnum || (OrderDirectionEnum = {}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,GAAG,CAUvD"}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.deepMerge = deepMerge;
|
|
4
|
-
function deepMerge(target, source) {
|
|
1
|
+
export function deepMerge(target, source) {
|
|
5
2
|
for (const key in source) {
|
|
6
3
|
if (source[key] instanceof Object && key in target) {
|
|
7
4
|
Object.assign(source[key], deepMerge(target[key], source[key]));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"where-builder.d.ts","sourceRoot":"","sources":["../../src/lib/where-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAGtE,MAAM,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC,CAAC;AAEjJ,qBAAa,YAAY;IAErB,OAAO,CAAC,WAAW,CAA2B;gBAElC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM;IAIhD,OAAO,CAAC,UAAU;IAIlB,KAAK,IAAI,IAAI;IAKb,KAAK,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK3C,QAAQ,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK9C,OAAO,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK7C,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAehC,aAAa,IAAI,OAAO;IAIxB,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAI/B,MAAM,IAAI,MAAM;IAIhB,OAAO,CAAC,cAAc;IAuDtB,OAAO,CAAC,eAAe;IAUvB;;OAEG;IACH,OAAO,CAAC,eAAe;CA0B1B"}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.WhereBuilder = void 0;
|
|
4
|
-
const types_1 = require("./types");
|
|
5
|
-
class WhereBuilder {
|
|
1
|
+
import { WhereLogicalOperatorEnum, WhereOperatorEnum } from './types';
|
|
2
|
+
export class WhereBuilder {
|
|
6
3
|
whereObject = {};
|
|
7
4
|
constructor(where) {
|
|
8
5
|
this.whereObject = typeof where === 'string' ? JSON.parse(where) : where || {};
|
|
@@ -19,11 +16,11 @@ class WhereBuilder {
|
|
|
19
16
|
return this;
|
|
20
17
|
}
|
|
21
18
|
andWhere(...args) {
|
|
22
|
-
this.parseCondition(
|
|
19
|
+
this.parseCondition(WhereLogicalOperatorEnum.AND, ...args);
|
|
23
20
|
return this;
|
|
24
21
|
}
|
|
25
22
|
orWhere(...args) {
|
|
26
|
-
this.parseCondition(
|
|
23
|
+
this.parseCondition(WhereLogicalOperatorEnum.OR, ...args);
|
|
27
24
|
return this;
|
|
28
25
|
}
|
|
29
26
|
removeWhere(field) {
|
|
@@ -83,14 +80,14 @@ class WhereBuilder {
|
|
|
83
80
|
this.updateCondition({ [field]: value }, type);
|
|
84
81
|
}
|
|
85
82
|
else {
|
|
86
|
-
this.updateCondition({ [field]: { [
|
|
83
|
+
this.updateCondition({ [field]: { [WhereOperatorEnum.EQ]: value } }, type);
|
|
87
84
|
}
|
|
88
85
|
}
|
|
89
86
|
else if (this.isOperator(value)) {
|
|
90
87
|
this.updateCondition({ [field]: { [value]: true } }, type);
|
|
91
88
|
}
|
|
92
89
|
else {
|
|
93
|
-
this.updateCondition({ [field]: { [
|
|
90
|
+
this.updateCondition({ [field]: { [WhereOperatorEnum.EQ]: value } }, type);
|
|
94
91
|
}
|
|
95
92
|
}
|
|
96
93
|
else {
|
|
@@ -147,4 +144,3 @@ class WhereBuilder {
|
|
|
147
144
|
}
|
|
148
145
|
}
|
|
149
146
|
}
|
|
150
|
-
exports.WhereBuilder = WhereBuilder;
|
package/package.json
CHANGED
|
@@ -1,10 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ackplus/nest-crud-request",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.16",
|
|
4
|
+
"description": "Framework-agnostic query builder for REST APIs - build complex queries with filtering, relations, and pagination for both frontend and backend",
|
|
4
5
|
"type": "commonjs",
|
|
5
6
|
"main": "./src/index.js",
|
|
6
7
|
"types": "./src/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"clean": "rm -rf dist",
|
|
10
|
+
"build": "tsc -p tsconfig.build.json",
|
|
11
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
12
|
+
"lint": "eslint \"src/**/*.ts\" --fix",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"query-builder",
|
|
22
|
+
"rest",
|
|
23
|
+
"api",
|
|
24
|
+
"frontend",
|
|
25
|
+
"backend",
|
|
26
|
+
"filtering",
|
|
27
|
+
"pagination",
|
|
28
|
+
"relations",
|
|
29
|
+
"typescript",
|
|
30
|
+
"nestjs",
|
|
31
|
+
"react",
|
|
32
|
+
"angular",
|
|
33
|
+
"vue"
|
|
34
|
+
],
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/ack-solutions/nest-crud"
|
|
38
|
+
},
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"author": "Ackplus",
|
|
7
41
|
"dependencies": {
|
|
8
42
|
"tslib": "^2.3.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"typescript": "^5.7.3"
|
|
9
46
|
}
|
|
10
|
-
}
|
|
47
|
+
}
|
package/src/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../packages/nest-crud-request/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,aAAa,CAAC"}
|
package/src/index.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
|
-
tslib_1.__exportStar(require("./lib/query-builder"), exports);
|
|
5
|
-
tslib_1.__exportStar(require("./lib/where-builder"), exports);
|
|
6
|
-
tslib_1.__exportStar(require("./lib/types"), exports);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"query-builder.d.ts","sourceRoot":"","sources":["../../../../../packages/nest-crud-request/src/lib/query-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElE,OAAO,EAAgB,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAGtE,qBAAa,YAAY;IAErB,OAAO,CAAC,OAAO,CAA2B;IAE1C,OAAO,CAAC,YAAY,CAAoC;IAExD,OAAO,CAAC,eAAe,CAA0C;gBAErD,OAAO,CAAC,EAAE,mBAAmB;IAMzC,UAAU,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI;IAO9C,YAAY,CAAC,OAAO,EAAE,mBAAmB,EAAE,IAAI,UAAQ,GAAG,IAAI;IAc9D,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IAa1C,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IAY7C,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAKnF,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKtC,KAAK,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK3C,QAAQ,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK9C,OAAO,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK7C,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,IAAI;IAQ1D,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAOlC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAK3B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAK3B,cAAc,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI;IAK1C,cAAc,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI;IAK1C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAKlC,QAAQ,CAAC,uBAAuB,UAAQ;;;;;;;;;;;IAoDxC,MAAM;CAKT"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"relation-builder.d.ts","sourceRoot":"","sources":["../../../../../packages/nest-crud-request/src/lib/relation-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAuB,eAAe,EAAE,MAAM,SAAS,CAAC;AAG/E,qBAAa,eAAe;IAExB,OAAO,CAAC,SAAS,CAAsB;gBAE3B,SAAS,CAAC,EAAE,eAAe,GAAG,MAAM;IAOhD,YAAY,CAAC,SAAS,EAAE,eAAe,GAAG,IAAI;IAe9C,KAAK,IAAI,IAAI;IAKb,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAgB3E,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK9B,YAAY,IAAI,OAAO;IAIvB,QAAQ,IAAI,cAAc;IAI1B,MAAM,IAAI,MAAM;CAInB"}
|
package/src/lib/types.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../packages/nest-crud-request/src/lib/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,eAAe,GAAG,MAAM,CAAC;IACrC,KAAK,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,MAAM,CAAC;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,oBAAY,wBAAwB;IAChC,GAAG,SAAS;IACZ,EAAE,QAAQ;CACb;AAED,oBAAY,iBAAiB;IACzB,EAAE,QAAQ;IACV,MAAM,QAAQ;IACd,EAAE,QAAQ;IACV,QAAQ,SAAS;IACjB,EAAE,QAAQ;IACV,QAAQ,SAAS;IACjB,EAAE,QAAQ;IACV,MAAM,WAAW;IACjB,IAAI,UAAU;IACd,QAAQ,aAAa;IACrB,KAAK,WAAW;IAChB,SAAS,cAAc;IACvB,OAAO,YAAY;IACnB,WAAW,eAAe;IAC1B,OAAO,aAAa;IACpB,WAAW,gBAAgB;IAC3B,OAAO,YAAY;IACnB,QAAQ,aAAa;CACxB;AAED,oBAAY,kBAAkB;IAC1B,GAAG,QAAQ;IACX,IAAI,SAAS;CAChB;AAED,MAAM,MAAM,WAAW,GAAG;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,IAAI,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE,CAAC;IACnC,GAAG,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,WAAW,EAAE,CAAC;AAGvD,MAAM,MAAM,mBAAmB,GAAG;IAC9B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,CAAC;AAE3E,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,cAAc,CAAC"}
|
package/src/lib/utils.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../packages/nest-crud-request/src/lib/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,GAAG,CAUvD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"where-builder.d.ts","sourceRoot":"","sources":["../../../../../packages/nest-crud-request/src/lib/where-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAGtE,MAAM,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC,CAAC;AAEjJ,qBAAa,YAAY;IAErB,OAAO,CAAC,WAAW,CAA2B;gBAElC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM;IAIhD,OAAO,CAAC,UAAU;IAIlB,KAAK,IAAI,IAAI;IAKb,KAAK,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK3C,QAAQ,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK9C,OAAO,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK7C,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAehC,aAAa,IAAI,OAAO;IAIxB,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAI/B,MAAM,IAAI,MAAM;IAIhB,OAAO,CAAC,cAAc;IAuDtB,OAAO,CAAC,eAAe;IAUvB;;OAEG;IACH,OAAO,CAAC,eAAe;CA0B1B"}
|
/package/{src → dist}/index.d.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|