@venizia/ignis-docs 0.0.3 → 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 (131) 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 +406 -17
  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 +2 -2
  21. package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
  22. package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
  23. package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
  24. package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
  25. package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
  26. package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
  27. package/wiki/changelogs/index.md +6 -0
  28. package/wiki/changelogs/planned-schema-migrator.md +0 -8
  29. package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
  30. package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
  31. package/wiki/guides/core-concepts/components-guide.md +509 -0
  32. package/wiki/{get-started → guides}/core-concepts/components.md +24 -17
  33. package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
  34. package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
  35. package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
  36. package/wiki/guides/core-concepts/persistent/index.md +119 -0
  37. package/wiki/guides/core-concepts/persistent/models.md +241 -0
  38. package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
  39. package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
  40. package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
  41. package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
  42. package/wiki/guides/get-started/philosophy.md +682 -0
  43. package/wiki/guides/get-started/setup.md +157 -0
  44. package/wiki/guides/index.md +89 -0
  45. package/wiki/guides/reference/glossary.md +243 -0
  46. package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
  47. package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
  48. package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
  49. package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
  50. package/wiki/guides/tutorials/realtime-chat.md +1261 -0
  51. package/wiki/guides/tutorials/testing.md +723 -0
  52. package/wiki/index.md +176 -37
  53. package/wiki/references/base/application.md +27 -0
  54. package/wiki/references/base/bootstrapping.md +30 -26
  55. package/wiki/references/base/components.md +24 -7
  56. package/wiki/references/base/controllers.md +51 -20
  57. package/wiki/references/base/datasources.md +30 -0
  58. package/wiki/references/base/dependency-injection.md +39 -3
  59. package/wiki/references/base/filter-system/application-usage.md +224 -0
  60. package/wiki/references/base/filter-system/array-operators.md +132 -0
  61. package/wiki/references/base/filter-system/comparison-operators.md +109 -0
  62. package/wiki/references/base/filter-system/default-filter.md +428 -0
  63. package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
  64. package/wiki/references/base/filter-system/index.md +127 -0
  65. package/wiki/references/base/filter-system/json-filtering.md +197 -0
  66. package/wiki/references/base/filter-system/list-operators.md +71 -0
  67. package/wiki/references/base/filter-system/logical-operators.md +156 -0
  68. package/wiki/references/base/filter-system/null-operators.md +58 -0
  69. package/wiki/references/base/filter-system/pattern-matching.md +108 -0
  70. package/wiki/references/base/filter-system/quick-reference.md +431 -0
  71. package/wiki/references/base/filter-system/range-operators.md +63 -0
  72. package/wiki/references/base/filter-system/tips.md +190 -0
  73. package/wiki/references/base/filter-system/use-cases.md +452 -0
  74. package/wiki/references/base/index.md +90 -0
  75. package/wiki/references/base/middlewares.md +602 -0
  76. package/wiki/references/base/models.md +215 -23
  77. package/wiki/references/base/providers.md +732 -0
  78. package/wiki/references/base/repositories/advanced.md +555 -0
  79. package/wiki/references/base/repositories/index.md +228 -0
  80. package/wiki/references/base/repositories/mixins.md +331 -0
  81. package/wiki/references/base/repositories/relations.md +486 -0
  82. package/wiki/references/base/repositories.md +40 -635
  83. package/wiki/references/base/services.md +28 -4
  84. package/wiki/references/components/authentication.md +22 -2
  85. package/wiki/references/components/health-check.md +12 -0
  86. package/wiki/references/components/index.md +23 -0
  87. package/wiki/references/components/mail.md +687 -0
  88. package/wiki/references/components/request-tracker.md +16 -0
  89. package/wiki/references/components/socket-io.md +18 -0
  90. package/wiki/references/components/static-asset.md +14 -26
  91. package/wiki/references/components/swagger.md +17 -0
  92. package/wiki/references/configuration/environment-variables.md +427 -0
  93. package/wiki/references/configuration/index.md +73 -0
  94. package/wiki/references/helpers/cron.md +14 -0
  95. package/wiki/references/helpers/crypto.md +15 -0
  96. package/wiki/references/helpers/env.md +16 -0
  97. package/wiki/references/helpers/error.md +17 -0
  98. package/wiki/references/helpers/index.md +14 -0
  99. package/wiki/references/helpers/inversion.md +24 -4
  100. package/wiki/references/helpers/logger.md +19 -0
  101. package/wiki/references/helpers/network.md +11 -0
  102. package/wiki/references/helpers/queue.md +19 -0
  103. package/wiki/references/helpers/redis.md +21 -0
  104. package/wiki/references/helpers/socket-io.md +24 -5
  105. package/wiki/references/helpers/storage.md +18 -10
  106. package/wiki/references/helpers/testing.md +18 -0
  107. package/wiki/references/helpers/types.md +16 -0
  108. package/wiki/references/helpers/uid.md +167 -0
  109. package/wiki/references/helpers/worker-thread.md +16 -0
  110. package/wiki/references/index.md +177 -0
  111. package/wiki/references/quick-reference.md +634 -0
  112. package/wiki/references/src-details/boot.md +3 -3
  113. package/wiki/references/src-details/dev-configs.md +0 -4
  114. package/wiki/references/src-details/docs.md +2 -2
  115. package/wiki/references/src-details/index.md +86 -0
  116. package/wiki/references/src-details/inversion.md +1 -6
  117. package/wiki/references/src-details/mcp-server.md +3 -15
  118. package/wiki/references/utilities/index.md +86 -10
  119. package/wiki/references/utilities/jsx.md +577 -0
  120. package/wiki/references/utilities/request.md +0 -2
  121. package/wiki/references/utilities/statuses.md +740 -0
  122. package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
  123. package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
  124. package/wiki/get-started/best-practices/data-modeling.md +0 -177
  125. package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
  126. package/wiki/get-started/best-practices/performance-optimization.md +0 -97
  127. package/wiki/get-started/best-practices/security-guidelines.md +0 -99
  128. package/wiki/get-started/core-concepts/persistent.md +0 -539
  129. package/wiki/get-started/index.md +0 -65
  130. package/wiki/get-started/philosophy.md +0 -296
  131. package/wiki/get-started/prerequisites.md +0 -113
@@ -0,0 +1,109 @@
1
+ ---
2
+ title: Comparison Operators
3
+ description: Equality and comparison operators for filtering records
4
+ difficulty: intermediate
5
+ ---
6
+
7
+ # Comparison Operators
8
+
9
+ Equality and comparison operators for filtering records.
10
+
11
+
12
+ ## eq - Equal To
13
+
14
+ Matches records where field equals the value.
15
+
16
+ ```typescript
17
+ // Implicit equality
18
+ { where: { status: 'active' } }
19
+
20
+ // Explicit form
21
+ { where: { status: { eq: 'active' } } }
22
+
23
+ // SQL: WHERE "status" = 'active'
24
+ ```
25
+
26
+ **Special Cases:**
27
+ ```typescript
28
+ // Null equality
29
+ { where: { deletedAt: null } }
30
+ { where: { deletedAt: { eq: null } } }
31
+ // SQL: WHERE "deleted_at" IS NULL
32
+
33
+ // Array shorthand (becomes IN)
34
+ { where: { id: [1, 2, 3] } }
35
+ // SQL: WHERE "id" IN (1, 2, 3)
36
+ ```
37
+
38
+
39
+ ## ne / neq - Not Equal To
40
+
41
+ Matches records where field does NOT equal the value.
42
+
43
+ ```typescript
44
+ { where: { status: { ne: 'deleted' } } }
45
+ { where: { status: { neq: 'deleted' } } } // Alias
46
+
47
+ // SQL: WHERE "status" != 'deleted'
48
+
49
+ // Null handling
50
+ { where: { deletedAt: { ne: null } } }
51
+ // SQL: WHERE "deleted_at" IS NOT NULL
52
+ ```
53
+
54
+
55
+ ## gt - Greater Than
56
+
57
+ ```typescript
58
+ // Numbers
59
+ { where: { price: { gt: 100 } } }
60
+ // SQL: WHERE "price" > 100
61
+
62
+ // Dates
63
+ { where: { createdAt: { gt: new Date('2024-01-01') } } }
64
+ // SQL: WHERE "created_at" > '2024-01-01'
65
+
66
+ // Strings (lexicographic)
67
+ { where: { name: { gt: 'M' } } }
68
+ // SQL: WHERE "name" > 'M'
69
+ ```
70
+
71
+
72
+ ## gte - Greater Than or Equal
73
+
74
+ ```typescript
75
+ { where: { quantity: { gte: 10 } } }
76
+ // SQL: WHERE "quantity" >= 10
77
+
78
+ // Combined with other operators
79
+ { where: { age: { gte: 18, lt: 65 } } }
80
+ // SQL: WHERE "age" >= 18 AND "age" < 65
81
+ ```
82
+
83
+
84
+ ## lt - Less Than
85
+
86
+ ```typescript
87
+ { where: { stock: { lt: 5 } } }
88
+ // SQL: WHERE "stock" < 5
89
+ ```
90
+
91
+
92
+ ## lte - Less Than or Equal
93
+
94
+ ```typescript
95
+ { where: { rating: { lte: 3 } } }
96
+ // SQL: WHERE "rating" <= 3
97
+ ```
98
+
99
+
100
+ ## Summary
101
+
102
+ | Operator | SQL | Description |
103
+ |----------|-----|-------------|
104
+ | `eq` | `=` | Equal to |
105
+ | `ne` / `neq` | `!=` | Not equal to |
106
+ | `gt` | `>` | Greater than |
107
+ | `gte` | `>=` | Greater than or equal |
108
+ | `lt` | `<` | Less than |
109
+ | `lte` | `<=` | Less than or equal |
@@ -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
+ ```