@venizia/ignis-docs 0.0.2 → 0.0.4-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.
Files changed (134) hide show
  1. package/README.md +1 -1
  2. package/package.json +4 -2
  3. package/wiki/best-practices/api-usage-examples.md +591 -0
  4. package/wiki/best-practices/architectural-patterns.md +415 -0
  5. package/wiki/best-practices/architecture-decisions.md +488 -0
  6. package/wiki/{get-started/best-practices → best-practices}/code-style-standards.md +647 -182
  7. package/wiki/{get-started/best-practices → best-practices}/common-pitfalls.md +109 -4
  8. package/wiki/{get-started/best-practices → best-practices}/contribution-workflow.md +34 -7
  9. package/wiki/best-practices/data-modeling.md +376 -0
  10. package/wiki/best-practices/deployment-strategies.md +698 -0
  11. package/wiki/best-practices/index.md +27 -0
  12. package/wiki/best-practices/performance-optimization.md +196 -0
  13. package/wiki/best-practices/security-guidelines.md +218 -0
  14. package/wiki/{get-started/best-practices → best-practices}/troubleshooting-tips.md +97 -1
  15. package/wiki/changelogs/2025-12-16-initial-architecture.md +1 -1
  16. package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +1 -1
  17. package/wiki/changelogs/2025-12-17-refactor.md +1 -1
  18. package/wiki/changelogs/2025-12-18-performance-optimizations.md +5 -5
  19. package/wiki/changelogs/2025-12-18-repository-validation-security.md +13 -7
  20. package/wiki/changelogs/2025-12-26-nested-relations-and-generics.md +86 -0
  21. package/wiki/changelogs/2025-12-26-transaction-support.md +57 -0
  22. package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
  23. package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
  24. package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
  25. package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
  26. package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
  27. package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
  28. package/wiki/changelogs/index.md +8 -1
  29. package/wiki/changelogs/planned-schema-migrator.md +2 -10
  30. package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
  31. package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
  32. package/wiki/guides/core-concepts/components-guide.md +509 -0
  33. package/wiki/guides/core-concepts/components.md +122 -0
  34. package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
  35. package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
  36. package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
  37. package/wiki/guides/core-concepts/persistent/index.md +119 -0
  38. package/wiki/guides/core-concepts/persistent/models.md +241 -0
  39. package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
  40. package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
  41. package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
  42. package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
  43. package/wiki/guides/get-started/philosophy.md +682 -0
  44. package/wiki/guides/get-started/setup.md +157 -0
  45. package/wiki/guides/index.md +89 -0
  46. package/wiki/guides/reference/glossary.md +243 -0
  47. package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
  48. package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
  49. package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
  50. package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
  51. package/wiki/guides/tutorials/realtime-chat.md +1261 -0
  52. package/wiki/guides/tutorials/testing.md +723 -0
  53. package/wiki/index.md +176 -37
  54. package/wiki/references/base/application.md +27 -0
  55. package/wiki/references/base/bootstrapping.md +30 -26
  56. package/wiki/references/base/components.md +532 -31
  57. package/wiki/references/base/controllers.md +136 -38
  58. package/wiki/references/base/datasources.md +108 -5
  59. package/wiki/references/base/dependency-injection.md +39 -3
  60. package/wiki/references/base/filter-system/application-usage.md +224 -0
  61. package/wiki/references/base/filter-system/array-operators.md +132 -0
  62. package/wiki/references/base/filter-system/comparison-operators.md +109 -0
  63. package/wiki/references/base/filter-system/default-filter.md +428 -0
  64. package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
  65. package/wiki/references/base/filter-system/index.md +127 -0
  66. package/wiki/references/base/filter-system/json-filtering.md +197 -0
  67. package/wiki/references/base/filter-system/list-operators.md +71 -0
  68. package/wiki/references/base/filter-system/logical-operators.md +156 -0
  69. package/wiki/references/base/filter-system/null-operators.md +58 -0
  70. package/wiki/references/base/filter-system/pattern-matching.md +108 -0
  71. package/wiki/references/base/filter-system/quick-reference.md +431 -0
  72. package/wiki/references/base/filter-system/range-operators.md +63 -0
  73. package/wiki/references/base/filter-system/tips.md +190 -0
  74. package/wiki/references/base/filter-system/use-cases.md +452 -0
  75. package/wiki/references/base/index.md +90 -0
  76. package/wiki/references/base/middlewares.md +602 -0
  77. package/wiki/references/base/models.md +215 -23
  78. package/wiki/references/base/providers.md +732 -0
  79. package/wiki/references/base/repositories/advanced.md +555 -0
  80. package/wiki/references/base/repositories/index.md +228 -0
  81. package/wiki/references/base/repositories/mixins.md +331 -0
  82. package/wiki/references/base/repositories/relations.md +486 -0
  83. package/wiki/references/base/repositories.md +40 -549
  84. package/wiki/references/base/services.md +28 -4
  85. package/wiki/references/components/authentication.md +22 -2
  86. package/wiki/references/components/health-check.md +12 -0
  87. package/wiki/references/components/index.md +23 -0
  88. package/wiki/references/components/mail.md +687 -0
  89. package/wiki/references/components/request-tracker.md +16 -0
  90. package/wiki/references/components/socket-io.md +18 -0
  91. package/wiki/references/components/static-asset.md +14 -26
  92. package/wiki/references/components/swagger.md +17 -0
  93. package/wiki/references/configuration/environment-variables.md +427 -0
  94. package/wiki/references/configuration/index.md +73 -0
  95. package/wiki/references/helpers/cron.md +14 -0
  96. package/wiki/references/helpers/crypto.md +15 -0
  97. package/wiki/references/helpers/env.md +16 -0
  98. package/wiki/references/helpers/error.md +17 -0
  99. package/wiki/references/helpers/index.md +15 -0
  100. package/wiki/references/helpers/inversion.md +24 -4
  101. package/wiki/references/helpers/logger.md +19 -0
  102. package/wiki/references/helpers/network.md +11 -0
  103. package/wiki/references/helpers/queue.md +19 -0
  104. package/wiki/references/helpers/redis.md +21 -0
  105. package/wiki/references/helpers/socket-io.md +24 -5
  106. package/wiki/references/helpers/storage.md +18 -10
  107. package/wiki/references/helpers/testing.md +18 -0
  108. package/wiki/references/helpers/types.md +167 -0
  109. package/wiki/references/helpers/uid.md +167 -0
  110. package/wiki/references/helpers/worker-thread.md +16 -0
  111. package/wiki/references/index.md +177 -0
  112. package/wiki/references/quick-reference.md +634 -0
  113. package/wiki/references/src-details/boot.md +3 -3
  114. package/wiki/references/src-details/dev-configs.md +0 -4
  115. package/wiki/references/src-details/docs.md +2 -2
  116. package/wiki/references/src-details/index.md +86 -0
  117. package/wiki/references/src-details/inversion.md +1 -6
  118. package/wiki/references/src-details/mcp-server.md +3 -15
  119. package/wiki/references/utilities/index.md +86 -10
  120. package/wiki/references/utilities/jsx.md +577 -0
  121. package/wiki/references/utilities/request.md +0 -2
  122. package/wiki/references/utilities/statuses.md +740 -0
  123. package/wiki/changelogs/planned-transaction-support.md +0 -216
  124. package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
  125. package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
  126. package/wiki/get-started/best-practices/data-modeling.md +0 -177
  127. package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
  128. package/wiki/get-started/best-practices/performance-optimization.md +0 -88
  129. package/wiki/get-started/best-practices/security-guidelines.md +0 -99
  130. package/wiki/get-started/core-concepts/components.md +0 -98
  131. package/wiki/get-started/core-concepts/persistent.md +0 -543
  132. package/wiki/get-started/index.md +0 -65
  133. package/wiki/get-started/philosophy.md +0 -296
  134. package/wiki/get-started/prerequisites.md +0 -113
@@ -0,0 +1,428 @@
1
+ ---
2
+ title: Default Filter
3
+ description: Automatically apply filter conditions to all repository queries
4
+ difficulty: intermediate
5
+ lastUpdated: 2026-01-02
6
+ ---
7
+
8
+ # Default Filter <Badge type="tip" text="v0.0.5+" />
9
+
10
+ Automatically apply filter conditions to all repository queries at the model level.
11
+
12
+ ::: info Added in v0.0.5
13
+ This feature was introduced in IGNIS v0.0.5 to support soft delete, multi-tenancy, and other automatic filtering patterns.
14
+ :::
15
+
16
+ > [!NOTE]
17
+ > Default filters are ideal for:
18
+ > - **Soft Delete**: Automatically exclude deleted records
19
+ > - **Multi-Tenancy**: Isolate data by tenant
20
+ > - **Active Records**: Filter to active/non-expired records
21
+ > - **Query Limits**: Prevent unbounded queries
22
+
23
+
24
+ ## Quick Start
25
+
26
+ Configure a default filter in your model:
27
+
28
+ ```typescript
29
+ import { model, BaseEntity } from '@venizia/ignis';
30
+ import { userTable } from '@/schemas';
31
+
32
+ @model({
33
+ type: 'entity',
34
+ settings: {
35
+ // Applied to all repository queries
36
+ defaultFilter: {
37
+ where: { isDeleted: false },
38
+ limit: 100,
39
+ },
40
+ },
41
+ })
42
+ export class User extends BaseEntity<typeof User.schema> {
43
+ static override schema = userTable;
44
+ }
45
+ ```
46
+
47
+ Now all queries automatically include the default filter:
48
+
49
+ ```typescript
50
+ // Your code
51
+ await userRepo.find({
52
+ filter: { where: { status: 'active' } }
53
+ });
54
+
55
+ // Actual query executed
56
+ // WHERE isDeleted = false AND status = 'active' LIMIT 100
57
+ ```
58
+
59
+
60
+ ## Configuration
61
+
62
+ ### Default Filter Properties
63
+
64
+ All standard filter properties are supported:
65
+
66
+ ```typescript
67
+ @model({
68
+ type: 'entity',
69
+ settings: {
70
+ defaultFilter: {
71
+ // WHERE conditions
72
+ where: { isDeleted: false, tenantId: 'tenant-123' },
73
+
74
+ // Maximum results (prevents unbounded queries)
75
+ limit: 100,
76
+
77
+ // Default pagination offset
78
+ offset: 0,
79
+
80
+ // Default sort order
81
+ order: ['createdAt DESC'],
82
+
83
+ // Default field selection
84
+ fields: ['id', 'name', 'email', 'createdAt'],
85
+
86
+ // Default relations to include
87
+ include: [{ relation: 'profile' }],
88
+ },
89
+ },
90
+ })
91
+ export class User extends BaseEntity<typeof User.schema> {}
92
+ ```
93
+
94
+
95
+ ## Merge Behavior
96
+
97
+ When a user provides a filter, it is merged with the default filter:
98
+
99
+ | Property | Merge Strategy |
100
+ |----------|----------------|
101
+ | `where` | **Deep merge** - user values override matching keys |
102
+ | `limit` | User replaces default (if provided) |
103
+ | `offset`/`skip` | User replaces default (if provided) |
104
+ | `order` | User replaces default (if provided) |
105
+ | `fields` | User replaces default (if provided) |
106
+ | `include` | User replaces default (if provided) |
107
+
108
+ ### Where Clause Merging
109
+
110
+ The `where` clause uses deep merge with user values taking precedence:
111
+
112
+ ```typescript
113
+ // Default filter
114
+ { where: { isDeleted: false, status: 'pending' }, limit: 100 }
115
+
116
+ // User filter
117
+ { where: { status: 'active', role: 'admin' }, limit: 10 }
118
+
119
+ // Merged result
120
+ {
121
+ where: {
122
+ isDeleted: false, // From default (preserved)
123
+ status: 'active', // User overrides default
124
+ role: 'admin' // From user (added)
125
+ },
126
+ limit: 10 // User overrides default
127
+ }
128
+ ```
129
+
130
+ ### Complex Where Conditions
131
+
132
+ ```typescript
133
+ // Default: soft delete and tenant isolation
134
+ const defaultFilter = {
135
+ where: {
136
+ isDeleted: false,
137
+ tenantId: 'tenant-123',
138
+ }
139
+ };
140
+
141
+ // User: OR conditions
142
+ const userFilter = {
143
+ where: {
144
+ or: [{ status: 'active' }, { priority: 'high' }]
145
+ }
146
+ };
147
+
148
+ // Result: AND of default + OR from user
149
+ // WHERE isDeleted = false AND tenantId = 'tenant-123'
150
+ // AND (status = 'active' OR priority = 'high')
151
+ ```
152
+
153
+ ### Operator Object Merging
154
+
155
+ Operator objects are deep merged, allowing range combinations:
156
+
157
+ ```typescript
158
+ // Default: created after 2024
159
+ const defaultFilter = {
160
+ where: {
161
+ createdAt: { gte: '2024-01-01' }
162
+ }
163
+ };
164
+
165
+ // User: created before end of 2024
166
+ const userFilter = {
167
+ where: {
168
+ createdAt: { lte: '2024-12-31' }
169
+ }
170
+ };
171
+
172
+ // Result: date range
173
+ {
174
+ where: {
175
+ createdAt: { gte: '2024-01-01', lte: '2024-12-31' }
176
+ }
177
+ }
178
+ ```
179
+
180
+
181
+ ## Bypassing Default Filter
182
+
183
+ Use `shouldSkipDefaultFilter: true` to bypass the default filter:
184
+
185
+ ```typescript
186
+ // Normal query - default filter applies
187
+ await repo.find({
188
+ filter: { where: { role: 'admin' } }
189
+ });
190
+ // WHERE isDeleted = false AND role = 'admin'
191
+
192
+ // Admin query - bypass default filter
193
+ await repo.find({
194
+ filter: { where: { role: 'admin' } },
195
+ options: { shouldSkipDefaultFilter: true }
196
+ });
197
+ // WHERE role = 'admin' (includes deleted records)
198
+ ```
199
+
200
+ ### Supported Operations
201
+
202
+ `shouldSkipDefaultFilter` works with all repository methods:
203
+
204
+ ```typescript
205
+ // Read operations
206
+ await repo.find({ filter, options: { shouldSkipDefaultFilter: true } });
207
+ await repo.findOne({ filter, options: { shouldSkipDefaultFilter: true } });
208
+ await repo.findById({ id, options: { shouldSkipDefaultFilter: true } });
209
+ await repo.count({ where, options: { shouldSkipDefaultFilter: true } });
210
+
211
+ // Update operations
212
+ await repo.updateById({ id, data, options: { shouldSkipDefaultFilter: true } });
213
+ await repo.updateAll({ where, data, options: { shouldSkipDefaultFilter: true } });
214
+
215
+ // Delete operations
216
+ await repo.deleteById({ id, options: { shouldSkipDefaultFilter: true } });
217
+ await repo.deleteAll({ where, options: { shouldSkipDefaultFilter: true, force: true } });
218
+ ```
219
+
220
+ ### Use Cases for Bypassing
221
+
222
+ | Scenario | Example |
223
+ |----------|---------|
224
+ | Admin dashboard | View all records including deleted |
225
+ | Data recovery | Restore soft-deleted records |
226
+ | Analytics | Count across all tenants |
227
+ | Data migration | Update records regardless of status |
228
+ | Audit logs | Access historical data |
229
+
230
+
231
+ ## Common Patterns
232
+
233
+ ### Soft Delete
234
+
235
+ ```typescript
236
+ @model({
237
+ type: 'entity',
238
+ settings: {
239
+ defaultFilter: {
240
+ where: { deletedAt: null }, // or { isDeleted: false }
241
+ },
242
+ },
243
+ })
244
+ export class Post extends BaseEntity<typeof Post.schema> {}
245
+
246
+ // All queries exclude deleted posts
247
+ await postRepo.find({ filter: {} });
248
+ // WHERE deletedAt IS NULL
249
+
250
+ // Restore a deleted post
251
+ await postRepo.updateById({
252
+ id: postId,
253
+ data: { deletedAt: null },
254
+ options: { shouldSkipDefaultFilter: true }
255
+ });
256
+ ```
257
+
258
+ ### Multi-Tenant Isolation
259
+
260
+ ```typescript
261
+ @model({
262
+ type: 'entity',
263
+ settings: {
264
+ defaultFilter: {
265
+ where: { tenantId: 'current-tenant' },
266
+ },
267
+ },
268
+ })
269
+ export class Document extends BaseEntity<typeof Document.schema> {}
270
+
271
+ // Queries scoped to tenant
272
+ await docRepo.find({ filter: { where: { type: 'invoice' } } });
273
+ // WHERE tenantId = 'current-tenant' AND type = 'invoice'
274
+
275
+ // Cross-tenant admin query
276
+ await docRepo.find({
277
+ filter: { where: { type: 'invoice' } },
278
+ options: { shouldSkipDefaultFilter: true }
279
+ });
280
+ // WHERE type = 'invoice'
281
+ ```
282
+
283
+ ### Active Records
284
+
285
+ ```typescript
286
+ @model({
287
+ type: 'entity',
288
+ settings: {
289
+ defaultFilter: {
290
+ where: {
291
+ isActive: true,
292
+ expiresAt: { gt: new Date().toISOString() },
293
+ },
294
+ limit: 50,
295
+ },
296
+ },
297
+ })
298
+ export class Subscription extends BaseEntity<typeof Subscription.schema> {}
299
+ ```
300
+
301
+ ### Query Limit Protection
302
+
303
+ ```typescript
304
+ @model({
305
+ type: 'entity',
306
+ settings: {
307
+ defaultFilter: {
308
+ limit: 1000, // Prevent unbounded queries
309
+ },
310
+ },
311
+ })
312
+ export class LogEntry extends BaseEntity<typeof LogEntry.schema> {}
313
+
314
+ // User can override limit, but there's always a sensible default
315
+ await logRepo.find({ filter: {} }); // LIMIT 1000
316
+ await logRepo.find({ filter: { limit: 50 } }); // LIMIT 50
317
+ ```
318
+
319
+
320
+ ## IExtraOptions Interface
321
+
322
+ The `shouldSkipDefaultFilter` option is part of the `IExtraOptions` interface:
323
+
324
+ ```typescript
325
+ interface IExtraOptions extends IWithTransaction {
326
+ /**
327
+ * If true, bypass the default filter configured in model settings.
328
+ */
329
+ shouldSkipDefaultFilter?: boolean;
330
+ }
331
+
332
+ interface IWithTransaction {
333
+ transaction?: ITransaction;
334
+ }
335
+ ```
336
+
337
+ This allows combining with transactions:
338
+
339
+ ```typescript
340
+ const tx = await repo.beginTransaction();
341
+
342
+ try {
343
+ // Both transaction and shouldSkipDefaultFilter
344
+ await repo.updateAll({
345
+ where: { status: 'archived' },
346
+ data: { isDeleted: true },
347
+ options: {
348
+ transaction: tx,
349
+ shouldSkipDefaultFilter: true,
350
+ }
351
+ });
352
+
353
+ await tx.commit();
354
+ } catch (e) {
355
+ await tx.rollback();
356
+ throw e;
357
+ }
358
+ ```
359
+
360
+
361
+ ## How It Works
362
+
363
+ ### Architecture
364
+
365
+ ```
366
+ +------------------+ +------------------+ +------------------+
367
+ | Model Settings | --> | DefaultFilterMixin | --> | Repository Method |
368
+ | defaultFilter | | applyDefaultFilter | | find/count/etc |
369
+ +------------------+ +------------------+ +------------------+
370
+ |
371
+ v
372
+ +------------------+
373
+ | FilterBuilder |
374
+ | mergeFilter() |
375
+ +------------------+
376
+ ```
377
+
378
+ ### DefaultFilterMixin
379
+
380
+ The `DefaultFilterMixin` provides:
381
+
382
+ ```typescript
383
+ // Check if default filter is configured
384
+ hasDefaultFilter(): boolean
385
+
386
+ // Get the raw default filter from model metadata
387
+ getDefaultFilter(): TFilter | undefined
388
+
389
+ // Merge default filter with user filter
390
+ applyDefaultFilter(opts: {
391
+ userFilter?: TFilter;
392
+ shouldSkipDefaultFilter?: boolean;
393
+ }): TFilter
394
+ ```
395
+
396
+ ### FilterBuilder.mergeFilter()
397
+
398
+ The merge logic is implemented in `FilterBuilder`:
399
+
400
+ ```typescript
401
+ const filterBuilder = new FilterBuilder();
402
+
403
+ const merged = filterBuilder.mergeFilter({
404
+ defaultFilter: { where: { isDeleted: false }, limit: 100 },
405
+ userFilter: { where: { status: 'active' }, limit: 10 }
406
+ });
407
+
408
+ // Result:
409
+ // { where: { isDeleted: false, status: 'active' }, limit: 10 }
410
+ ```
411
+
412
+
413
+ ## Quick Reference
414
+
415
+ | Want to... | Code |
416
+ |------------|------|
417
+ | Configure default filter | `@model({ settings: { defaultFilter: { ... } } })` |
418
+ | Bypass default filter | `options: { shouldSkipDefaultFilter: true }` |
419
+ | Combine with transaction | `options: { transaction: tx, shouldSkipDefaultFilter: true }` |
420
+ | Check if model has default | `repo.hasDefaultFilter()` |
421
+ | Get raw default filter | `repo.getDefaultFilter()` |
422
+
423
+
424
+ ## Next Steps
425
+
426
+ - [Filter System Overview](./index.md) - Filter structure and operators
427
+ - [Repository Mixins](../repositories/mixins.md) - Mixin architecture
428
+ - [Advanced Features](../repositories/advanced.md) - Transactions, hidden properties
@@ -0,0 +1,155 @@
1
+ ---
2
+ title: Fields, Ordering & Pagination
3
+ description: Control field selection, sorting, and pagination
4
+ difficulty: intermediate
5
+ ---
6
+
7
+ # Fields, Ordering & Pagination
8
+
9
+ Control which fields are returned, how results are sorted, and how to paginate.
10
+
11
+
12
+ ## Field Selection
13
+
14
+ Control which fields are returned using `fields`:
15
+
16
+ ### Array Format (Recommended)
17
+
18
+ ```typescript
19
+ await repo.find({
20
+ filter: {
21
+ where: { status: 'active' },
22
+ fields: ['id', 'email', 'name']
23
+ }
24
+ });
25
+ // Returns only: { id, email, name }
26
+ ```
27
+
28
+ ### Object Format
29
+
30
+ ```typescript
31
+ // Include specific fields
32
+ await repo.find({
33
+ filter: {
34
+ fields: { id: true, email: true, name: true }
35
+ }
36
+ });
37
+
38
+ // Exclude specific fields
39
+ await repo.find({
40
+ filter: {
41
+ fields: { password: false, secret: false }
42
+ }
43
+ });
44
+ ```
45
+
46
+
47
+ ## Ordering
48
+
49
+ ### Basic Ordering
50
+
51
+ ```typescript
52
+ // Single column, descending
53
+ await repo.find({
54
+ filter: { order: ['createdAt DESC'] }
55
+ });
56
+
57
+ // Multiple columns
58
+ await repo.find({
59
+ filter: { order: ['status ASC', 'createdAt DESC'] }
60
+ });
61
+
62
+ // Default direction is ASC
63
+ await repo.find({
64
+ filter: { order: ['name'] } // Same as 'name ASC'
65
+ });
66
+ ```
67
+
68
+ ### JSON Path Ordering
69
+
70
+ Order by nested fields in JSON columns:
71
+
72
+ ```typescript
73
+ await repo.find({
74
+ filter: { order: ['metadata.priority DESC'] }
75
+ });
76
+ // SQL: ORDER BY "metadata" #> '{priority}' DESC
77
+
78
+ await repo.find({
79
+ filter: { order: ['settings.display.theme ASC'] }
80
+ });
81
+ ```
82
+
83
+ ### JSONB Sort Order
84
+
85
+ | JSONB Type | Sort Order |
86
+ |------------|------------|
87
+ | `null` | First (lowest) |
88
+ | `boolean` | `false` < `true` |
89
+ | `number` | Numeric order |
90
+ | `string` | Lexicographic |
91
+ | `array` | Element-wise |
92
+ | `object` | Key-value |
93
+
94
+
95
+ ## Pagination
96
+
97
+ ### Limit and Skip
98
+
99
+ ```typescript
100
+ // First 10 results
101
+ await repo.find({
102
+ filter: { limit: 10 }
103
+ });
104
+
105
+ // Page 2 (skip first 10, get next 10)
106
+ await repo.find({
107
+ filter: { limit: 10, skip: 10 }
108
+ });
109
+
110
+ // Page N formula: skip = (page - 1) * limit
111
+ const page = 3;
112
+ const pageSize = 20;
113
+ await repo.find({
114
+ filter: {
115
+ limit: pageSize,
116
+ skip: (page - 1) * pageSize
117
+ }
118
+ });
119
+ ```
120
+
121
+ > [!TIP]
122
+ > Always use `limit` for public-facing endpoints to prevent memory exhaustion.
123
+
124
+ ### Pagination Helper
125
+
126
+ ```typescript
127
+ function getPaginationFilter(page: number, pageSize: number = 20) {
128
+ return {
129
+ limit: pageSize,
130
+ skip: (page - 1) * pageSize
131
+ };
132
+ }
133
+
134
+ // Usage
135
+ const filter = {
136
+ where: { status: 'active' },
137
+ ...getPaginationFilter(3, 20)
138
+ };
139
+ // { where: {...}, limit: 20, skip: 40 }
140
+ ```
141
+
142
+
143
+ ## Combined Example
144
+
145
+ ```typescript
146
+ await repo.find({
147
+ filter: {
148
+ where: { status: 'active' },
149
+ fields: ['id', 'name', 'price', 'createdAt'],
150
+ order: ['price ASC', 'createdAt DESC'],
151
+ limit: 20,
152
+ skip: 0
153
+ }
154
+ });
155
+ ```
@@ -0,0 +1,127 @@
1
+ ---
2
+ title: Filter System Overview
3
+ description: Complete reference for the IGNIS filter system
4
+ difficulty: intermediate
5
+ ---
6
+
7
+ # Filter System
8
+
9
+ Complete reference for the Ignis filter system - operators, JSON filtering, array operators, default filters, and query patterns.
10
+
11
+ > [!NOTE]
12
+ > If you're new to Ignis, start with:
13
+ > - [5-Minute Quickstart](/guides/get-started/5-minute-quickstart) - Get up and running
14
+ > - [Building a CRUD API](/guides/tutorials/building-a-crud-api) - Learn the basics
15
+ > - [Repositories](/references/base/repositories) - Repository overview
16
+
17
+ ## Prerequisites
18
+
19
+ Before reading this document, you should understand:
20
+
21
+ - [Repositories](../repositories/) - Basic repository operations (find, create, update, delete)
22
+ - [Models](../models.md) - Entity definitions and schemas
23
+ - SQL basics - Understanding of WHERE clauses and operators
24
+ - TypeScript type system - Type safety and inference
25
+
26
+ ## Documentation
27
+
28
+ | Guide | Description |
29
+ |-------|-------------|
30
+ | [**⚡ Quick Reference**](./quick-reference.md) | **Single-page cheat sheet of all operators** |
31
+ | [Comparison Operators](./comparison-operators.md) | Equality, range, null checks |
32
+ | [Pattern Matching](./pattern-matching.md) | LIKE, ILIKE, regex |
33
+ | [Logical Operators](./logical-operators.md) | AND, OR combinations |
34
+ | [List Operators](./list-operators.md) | IN, NOT IN |
35
+ | [Range Operators](./range-operators.md) | BETWEEN, NOT BETWEEN |
36
+ | [Null Operators](./null-operators.md) | IS NULL, IS NOT NULL |
37
+ | [Array Operators](./array-operators.md) | PostgreSQL array operations |
38
+ | [JSON Filtering](./json-filtering.md) | JSON/JSONB path queries |
39
+ | [Fields, Order, Pagination](./fields-order-pagination.md) | SELECT, ORDER BY, LIMIT |
40
+ | [**Default Filter**](./default-filter.md) | Automatic filter application |
41
+ | [Application Usage](./application-usage.md) | Filter flow in applications |
42
+ | [Tips & Best Practices](./tips.md) | Performance and patterns |
43
+ | [Use Cases](./use-cases.md) | Real-world examples |
44
+
45
+
46
+ ## Filter Structure
47
+
48
+ The `Filter<T>` object is the core mechanism for querying data in Ignis. It provides a structured, type-safe way to express complex queries without writing raw SQL.
49
+
50
+ ```typescript
51
+ type TFilter<T> = {
52
+ where?: TWhere<T>; // Query conditions (SQL WHERE)
53
+ fields?: TFields<T>; // Column selection (SQL SELECT)
54
+ order?: string[]; // Sorting (SQL ORDER BY)
55
+ limit?: number; // Max results (SQL LIMIT)
56
+ skip?: number; // Pagination offset (SQL OFFSET)
57
+ offset?: number; // Alias for skip
58
+ include?: TInclusion[]; // Related data (SQL JOIN / subqueries)
59
+ };
60
+ ```
61
+
62
+
63
+ ## SQL Mapping Overview
64
+
65
+ | Filter Property | SQL Equivalent | Purpose |
66
+ |-----------------|----------------|---------|
67
+ | `where` | `WHERE` | Filter rows by conditions |
68
+ | `fields` | `SELECT col1, col2` | Select specific columns |
69
+ | `order` | `ORDER BY` | Sort results |
70
+ | `limit` | `LIMIT` | Restrict number of results |
71
+ | `skip` / `offset` | `OFFSET` | Skip rows for pagination |
72
+ | `include` | `JOIN` / subquery | Include related data |
73
+
74
+
75
+ ## Basic Example
76
+
77
+ ```typescript
78
+ // Filter object
79
+ const filter = {
80
+ where: { status: 'active', role: 'admin' },
81
+ fields: ['id', 'name', 'email'],
82
+ order: ['createdAt DESC'],
83
+ limit: 10,
84
+ skip: 0
85
+ };
86
+
87
+ // Equivalent SQL
88
+ // SELECT "id", "name", "email"
89
+ // FROM "User"
90
+ // WHERE "status" = 'active' AND "role" = 'admin'
91
+ // ORDER BY "created_at" DESC
92
+ // LIMIT 10 OFFSET 0
93
+ ```
94
+
95
+
96
+ ## Quick Reference
97
+
98
+ | Want to... | Filter Syntax |
99
+ |------------|---------------|
100
+ | Equals | `{ field: value }` or `{ field: { eq: value } }` |
101
+ | Not equals | `{ field: { ne: value } }` |
102
+ | Greater than | `{ field: { gt: value } }` |
103
+ | Greater or equal | `{ field: { gte: value } }` |
104
+ | Less than | `{ field: { lt: value } }` |
105
+ | Less or equal | `{ field: { lte: value } }` |
106
+ | Is null | `{ field: null }` or `{ field: { is: null } }` |
107
+ | Is not null | `{ field: { isn: null } }` |
108
+ | In list | `{ field: { in: [a, b, c] } }` |
109
+ | Not in list | `{ field: { nin: [a, b, c] } }` |
110
+ | Range | `{ field: { between: [min, max] } }` |
111
+ | Outside range | `{ field: { notBetween: [min, max] } }` |
112
+ | Contains pattern | `{ field: { like: '%pattern%' } }` |
113
+ | Case-insensitive | `{ field: { ilike: '%pattern%' } }` |
114
+ | Regex match | `{ field: { regexp: '^pattern$' } }` |
115
+ | Array contains all | `{ arrayField: { contains: [a, b] } }` |
116
+ | Array is subset | `{ arrayField: { containedBy: [a, b, c] } }` |
117
+ | Array overlaps | `{ arrayField: { overlaps: [a, b] } }` |
118
+ | JSON nested | `{ 'jsonField.nested.path': value }` |
119
+ | JSON with operator | `{ 'jsonField.path': { gt: 10 } }` |
120
+ | AND conditions | `{ a: 1, b: 2 }` or `{ and: [{a: 1}, {b: 2}] }` |
121
+ | OR conditions | `{ or: [{ a: 1 }, { b: 2 }] }` |
122
+ | Include relation | `{ include: [{ relation: 'name' }] }` |
123
+ | Nested include | `{ include: [{ relation: 'a', scope: { include: [{ relation: 'b' }] } }] }` |
124
+ | Select fields | `{ fields: ['id', 'name'] }` |
125
+ | Order by | `{ order: ['field DESC'] }` |
126
+ | Order by JSON | `{ order: ['jsonField.path DESC'] }` |
127
+ | Paginate | `{ limit: 10, skip: 20 }` |