@venizia/ignis-docs 0.0.7 → 0.0.8-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 (158) hide show
  1. package/dist/mcp-server/common/paths.d.ts +4 -2
  2. package/dist/mcp-server/common/paths.d.ts.map +1 -1
  3. package/dist/mcp-server/common/paths.js +8 -6
  4. package/dist/mcp-server/common/paths.js.map +1 -1
  5. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts +1 -1
  6. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts.map +1 -1
  7. package/dist/mcp-server/tools/docs/get-document-content.tool.js +7 -7
  8. package/dist/mcp-server/tools/docs/get-document-metadata.tool.js +3 -3
  9. package/dist/mcp-server/tools/docs/get-package-overview.tool.d.ts +1 -1
  10. package/dist/mcp-server/tools/docs/get-package-overview.tool.js +1 -1
  11. package/package.json +1 -1
  12. package/wiki/best-practices/api-usage-examples.md +9 -9
  13. package/wiki/best-practices/architectural-patterns.md +19 -3
  14. package/wiki/best-practices/architecture-decisions.md +6 -6
  15. package/wiki/best-practices/code-style-standards/advanced-patterns.md +1 -1
  16. package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
  17. package/wiki/best-practices/code-style-standards/function-patterns.md +2 -2
  18. package/wiki/best-practices/code-style-standards/index.md +2 -2
  19. package/wiki/best-practices/code-style-standards/naming-conventions.md +1 -1
  20. package/wiki/best-practices/code-style-standards/route-definitions.md +4 -4
  21. package/wiki/best-practices/data-modeling.md +1 -1
  22. package/wiki/best-practices/deployment-strategies.md +1 -1
  23. package/wiki/best-practices/error-handling.md +2 -2
  24. package/wiki/best-practices/performance-optimization.md +3 -3
  25. package/wiki/best-practices/security-guidelines.md +2 -2
  26. package/wiki/best-practices/troubleshooting-tips.md +1 -1
  27. package/wiki/{references → extensions}/components/authentication/api.md +12 -20
  28. package/wiki/{references → extensions}/components/authentication/errors.md +1 -1
  29. package/wiki/{references → extensions}/components/authentication/index.md +5 -8
  30. package/wiki/{references → extensions}/components/authentication/usage.md +20 -36
  31. package/wiki/{references → extensions}/components/authorization/api.md +62 -13
  32. package/wiki/{references → extensions}/components/authorization/errors.md +12 -7
  33. package/wiki/{references → extensions}/components/authorization/index.md +93 -6
  34. package/wiki/{references → extensions}/components/authorization/usage.md +42 -4
  35. package/wiki/{references → extensions}/components/health-check.md +5 -4
  36. package/wiki/{references → extensions}/components/index.md +2 -0
  37. package/wiki/{references → extensions}/components/mail/index.md +1 -1
  38. package/wiki/{references → extensions}/components/request-tracker.md +1 -1
  39. package/wiki/{references → extensions}/components/socket-io/api.md +2 -2
  40. package/wiki/{references → extensions}/components/socket-io/errors.md +2 -0
  41. package/wiki/{references → extensions}/components/socket-io/index.md +24 -20
  42. package/wiki/{references → extensions}/components/socket-io/usage.md +2 -2
  43. package/wiki/{references → extensions}/components/static-asset/api.md +14 -15
  44. package/wiki/{references → extensions}/components/static-asset/errors.md +3 -1
  45. package/wiki/{references → extensions}/components/static-asset/index.md +158 -89
  46. package/wiki/{references → extensions}/components/static-asset/usage.md +8 -5
  47. package/wiki/{references → extensions}/components/swagger.md +3 -3
  48. package/wiki/{references → extensions}/components/template/index.md +4 -4
  49. package/wiki/{references → extensions}/components/template/setup-page.md +1 -1
  50. package/wiki/{references → extensions}/components/template/single-page.md +1 -1
  51. package/wiki/{references → extensions}/components/websocket/api.md +7 -6
  52. package/wiki/{references → extensions}/components/websocket/errors.md +17 -3
  53. package/wiki/{references → extensions}/components/websocket/index.md +17 -11
  54. package/wiki/{references → extensions}/components/websocket/usage.md +2 -2
  55. package/wiki/{references → extensions}/helpers/crypto/index.md +1 -1
  56. package/wiki/{references → extensions}/helpers/env/index.md +9 -5
  57. package/wiki/{references → extensions}/helpers/error/index.md +2 -7
  58. package/wiki/{references → extensions}/helpers/index.md +18 -6
  59. package/wiki/{references → extensions}/helpers/kafka/admin.md +13 -1
  60. package/wiki/{references → extensions}/helpers/kafka/consumer.md +28 -28
  61. package/wiki/{references → extensions}/helpers/kafka/examples.md +19 -19
  62. package/wiki/{references → extensions}/helpers/kafka/index.md +51 -48
  63. package/wiki/{references → extensions}/helpers/kafka/producer.md +18 -18
  64. package/wiki/{references → extensions}/helpers/kafka/schema-registry.md +25 -25
  65. package/wiki/{references → extensions}/helpers/logger/index.md +2 -2
  66. package/wiki/{references → extensions}/helpers/queue/index.md +400 -4
  67. package/wiki/{references → extensions}/helpers/storage/api.md +170 -10
  68. package/wiki/{references → extensions}/helpers/storage/index.md +44 -8
  69. package/wiki/{references → extensions}/helpers/template/index.md +1 -1
  70. package/wiki/{references → extensions}/helpers/testing/index.md +4 -4
  71. package/wiki/{references → extensions}/helpers/types/index.md +63 -16
  72. package/wiki/{references → extensions}/helpers/websocket/index.md +1 -1
  73. package/wiki/extensions/index.md +48 -0
  74. package/wiki/guides/core-concepts/application/bootstrapping.md +55 -37
  75. package/wiki/guides/core-concepts/application/index.md +95 -35
  76. package/wiki/guides/core-concepts/components-guide.md +23 -19
  77. package/wiki/guides/core-concepts/components.md +34 -10
  78. package/wiki/guides/core-concepts/dependency-injection.md +99 -34
  79. package/wiki/guides/core-concepts/grpc-controllers.md +295 -0
  80. package/wiki/guides/core-concepts/persistent/datasources.md +27 -8
  81. package/wiki/guides/core-concepts/persistent/models.md +43 -1
  82. package/wiki/guides/core-concepts/persistent/repositories.md +75 -8
  83. package/wiki/guides/core-concepts/persistent/transactions.md +38 -8
  84. package/wiki/guides/core-concepts/{controllers.md → rest-controllers.md} +30 -33
  85. package/wiki/guides/core-concepts/services.md +19 -5
  86. package/wiki/guides/get-started/5-minute-quickstart.md +6 -7
  87. package/wiki/guides/get-started/philosophy.md +1 -1
  88. package/wiki/guides/index.md +2 -2
  89. package/wiki/guides/reference/glossary.md +7 -7
  90. package/wiki/guides/reference/mcp-docs-server.md +1 -1
  91. package/wiki/guides/tutorials/building-a-crud-api.md +2 -2
  92. package/wiki/guides/tutorials/complete-installation.md +17 -14
  93. package/wiki/guides/tutorials/ecommerce-api.md +18 -18
  94. package/wiki/guides/tutorials/realtime-chat.md +8 -8
  95. package/wiki/guides/tutorials/testing.md +2 -2
  96. package/wiki/index.md +4 -3
  97. package/wiki/references/base/application.md +341 -21
  98. package/wiki/references/base/bootstrapping.md +43 -13
  99. package/wiki/references/base/components.md +259 -8
  100. package/wiki/references/base/controllers.md +556 -253
  101. package/wiki/references/base/datasources.md +159 -79
  102. package/wiki/references/base/dependency-injection.md +299 -48
  103. package/wiki/references/base/filter-system/application-usage.md +18 -2
  104. package/wiki/references/base/filter-system/array-operators.md +14 -6
  105. package/wiki/references/base/filter-system/comparison-operators.md +9 -3
  106. package/wiki/references/base/filter-system/default-filter.md +28 -3
  107. package/wiki/references/base/filter-system/fields-order-pagination.md +17 -13
  108. package/wiki/references/base/filter-system/index.md +169 -11
  109. package/wiki/references/base/filter-system/json-filtering.md +51 -18
  110. package/wiki/references/base/filter-system/list-operators.md +4 -3
  111. package/wiki/references/base/filter-system/logical-operators.md +7 -2
  112. package/wiki/references/base/filter-system/null-operators.md +50 -0
  113. package/wiki/references/base/filter-system/quick-reference.md +82 -243
  114. package/wiki/references/base/filter-system/range-operators.md +7 -1
  115. package/wiki/references/base/filter-system/tips.md +34 -7
  116. package/wiki/references/base/filter-system/use-cases.md +6 -5
  117. package/wiki/references/base/grpc-controllers.md +984 -0
  118. package/wiki/references/base/index.md +32 -24
  119. package/wiki/references/base/middleware.md +347 -0
  120. package/wiki/references/base/models.md +390 -46
  121. package/wiki/references/base/providers.md +14 -14
  122. package/wiki/references/base/repositories/advanced.md +84 -69
  123. package/wiki/references/base/repositories/index.md +447 -12
  124. package/wiki/references/base/repositories/mixins.md +103 -98
  125. package/wiki/references/base/repositories/relations.md +129 -45
  126. package/wiki/references/base/repositories/soft-deletable.md +104 -23
  127. package/wiki/references/base/services.md +94 -14
  128. package/wiki/references/index.md +12 -10
  129. package/wiki/references/quick-reference.md +98 -65
  130. package/wiki/references/utilities/crypto.md +21 -4
  131. package/wiki/references/utilities/date.md +25 -7
  132. package/wiki/references/utilities/index.md +26 -24
  133. package/wiki/references/utilities/jsx.md +54 -54
  134. package/wiki/references/utilities/module.md +8 -6
  135. package/wiki/references/utilities/parse.md +16 -9
  136. package/wiki/references/utilities/performance.md +22 -7
  137. package/wiki/references/utilities/promise.md +19 -16
  138. package/wiki/references/utilities/request.md +48 -26
  139. package/wiki/references/utilities/schema.md +69 -6
  140. package/wiki/references/utilities/statuses.md +131 -140
  141. /package/wiki/{references → extensions}/components/mail/api.md +0 -0
  142. /package/wiki/{references → extensions}/components/mail/errors.md +0 -0
  143. /package/wiki/{references → extensions}/components/mail/usage.md +0 -0
  144. /package/wiki/{references → extensions}/components/template/api-page.md +0 -0
  145. /package/wiki/{references → extensions}/components/template/errors-page.md +0 -0
  146. /package/wiki/{references → extensions}/components/template/usage-page.md +0 -0
  147. /package/wiki/{references → extensions}/helpers/cron/index.md +0 -0
  148. /package/wiki/{references → extensions}/helpers/inversion/index.md +0 -0
  149. /package/wiki/{references → extensions}/helpers/network/api.md +0 -0
  150. /package/wiki/{references → extensions}/helpers/network/index.md +0 -0
  151. /package/wiki/{references → extensions}/helpers/redis/index.md +0 -0
  152. /package/wiki/{references → extensions}/helpers/socket-io/api.md +0 -0
  153. /package/wiki/{references → extensions}/helpers/socket-io/index.md +0 -0
  154. /package/wiki/{references → extensions}/helpers/template/single-page.md +0 -0
  155. /package/wiki/{references → extensions}/helpers/uid/index.md +0 -0
  156. /package/wiki/{references → extensions}/helpers/websocket/api.md +0 -0
  157. /package/wiki/{references → extensions}/helpers/worker-thread/index.md +0 -0
  158. /package/wiki/{references → extensions}/src-details/mcp-server.md +0 -0
@@ -29,150 +29,153 @@ Ignis uses the mixin pattern to compose repository features. This enables:
29
29
 
30
30
  | Mixin | Responsibility |
31
31
  |-------|----------------|
32
+ | `FieldsVisibilityMixin` | Hidden properties exclusion at SQL level |
32
33
  | `DefaultFilterMixin` | Automatic filter application from model settings |
33
- | `FieldsVisibilityMixin` | Hidden properties exclusion |
34
34
 
35
35
 
36
- ## DefaultFilterMixin
36
+ ## FieldsVisibilityMixin
37
37
 
38
- Provides automatic default filter application for all repository queries.
38
+ Provides hidden properties management for SQL-level field exclusion. Reads `hiddenProperties` from `@model` metadata settings and builds a visible columns map for Drizzle's `select()` and `returning()` calls.
39
39
 
40
- **File:** `packages/core/src/base/repositories/mixins/default-filter.ts`
40
+ **File:** `packages/core/src/base/repositories/mixins/fields-visibility.ts`
41
+
42
+ ### Abstract Requirements
43
+
44
+ Classes using this mixin must implement:
45
+
46
+ ```typescript
47
+ abstract getEntity(): BaseEntity<TTableSchemaWithId>;
48
+ ```
41
49
 
42
50
  ### Properties
43
51
 
44
52
  | Property | Type | Description |
45
53
  |----------|------|-------------|
46
- | `_defaultFilter` | `TFilter \| null \| undefined` | Cached default filter |
54
+ | `_hiddenProperties` | `Set<string> \| null` | Cached hidden property names (`null` = not yet computed) |
55
+ | `_visibleProperties` | `Record<string, any> \| null \| undefined` | Cached visible columns (`null` = not yet computed, `undefined` = computed with no hidden props) |
47
56
 
48
57
  ### Methods
49
58
 
50
59
  | Method | Returns | Description |
51
60
  |--------|---------|-------------|
52
- | `getDefaultFilter()` | `TFilter \| undefined` | Get default filter from model metadata |
53
- | `hasDefaultFilter()` | `boolean` | Check if model has a default filter configured |
54
- | `applyDefaultFilter(opts)` | `TFilter` | Merge default filter with user filter |
61
+ | `get hiddenProperties` | `Set<string>` | Getter that delegates to `getHiddenProperties()` |
62
+ | `set hiddenProperties` | `void` | Override hidden properties set |
63
+ | `getHiddenProperties()` | `Set<string>` | Get hidden properties from model metadata (cached) |
64
+ | `hasHiddenProperties()` | `boolean` | Check if model has any hidden properties |
65
+ | `get visibleProperties` | `Record<string, any> \| undefined` | Getter that delegates to `getVisibleProperties()` |
66
+ | `set visibleProperties` | `void` | Override visible properties |
67
+ | `getVisibleProperties()` | `Record<string, any> \| undefined` | Build visible columns object for Drizzle (cached). Returns `undefined` if no hidden props. |
55
68
 
56
69
  ### Usage
57
70
 
58
71
  ```typescript
59
- import { DefaultFilterMixin } from '@venizia/ignis';
72
+ import { FieldsVisibilityMixin } from '@venizia/ignis';
60
73
  import { BaseHelper } from '@venizia/ignis-helpers';
61
74
 
62
- class MyRepository extends DefaultFilterMixin(BaseHelper) {
63
- // Required abstract implementations
75
+ class MyRepository extends FieldsVisibilityMixin(BaseHelper) {
76
+ // Required abstract implementation
64
77
  abstract getEntity(): BaseEntity;
65
- abstract get filterBuilder(): FilterBuilder;
66
78
 
67
79
  // Now has access to:
68
- // - getDefaultFilter()
69
- // - hasDefaultFilter()
70
- // - applyDefaultFilter()
80
+ // - hiddenProperties (getter/setter)
81
+ // - visibleProperties (getter/setter)
82
+ // - getHiddenProperties()
83
+ // - hasHiddenProperties()
84
+ // - getVisibleProperties()
71
85
  }
72
86
  ```
73
87
 
74
- ### applyDefaultFilter Options
88
+ ### Visible Properties for Drizzle
89
+
90
+ The `getVisibleProperties()` method returns a columns object for Drizzle's `select()` or `returning()`:
75
91
 
76
92
  ```typescript
77
- interface ApplyDefaultFilterOptions {
78
- userFilter?: TFilter; // User-provided filter
79
- shouldSkipDefaultFilter?: boolean; // If true, bypass default filter
80
- }
93
+ // Model with hiddenProperties: ['password', 'apiKey']
94
+ // Schema columns: { id, email, password, apiKey, createdAt }
95
+
96
+ const visibleProps = this.getVisibleProperties();
97
+ // Result: { id: column, email: column, createdAt: column }
98
+ // (password and apiKey excluded)
99
+
100
+ // Used in Drizzle queries
101
+ await connector.select(visibleProps).from(schema);
102
+ // SELECT id, email, created_at FROM users
81
103
  ```
82
104
 
83
- ### Implementation
105
+ ### How It Resolves Hidden Properties
84
106
 
85
- ```typescript
86
- applyDefaultFilter<DataObject = any>(opts: {
87
- userFilter?: TFilter<DataObject>;
88
- shouldSkipDefaultFilter?: boolean;
89
- }): TFilter<DataObject> {
90
- const { userFilter, shouldSkipDefaultFilter } = opts;
91
-
92
- // Skip default filter if explicitly requested
93
- if (shouldSkipDefaultFilter) {
94
- return userFilter ?? {};
95
- }
107
+ 1. Checks the cache (`_hiddenProperties`). If not `null`, returns cached value.
108
+ 2. Looks up the entity name in `MetadataRegistry.getModelEntry()`.
109
+ 3. Reads `metadata.settings.hiddenProperties` (array of field names).
110
+ 4. Converts to a `Set<string>` and caches.
96
111
 
97
- // Get default filter from model metadata
98
- const defaultFilter = this.getDefaultFilter();
99
112
 
100
- // No default filter configured - return user filter
101
- if (!defaultFilter) {
102
- return userFilter ?? {};
103
- }
113
+ ## DefaultFilterMixin
104
114
 
105
- // Merge default filter with user filter
106
- return this.filterBuilder.mergeFilter({ defaultFilter, userFilter });
107
- }
108
- ```
115
+ Provides automatic default filter application for all repository queries. Reads `defaultFilter` from `@model` metadata settings and merges it with user-provided filters.
109
116
 
117
+ **File:** `packages/core/src/base/repositories/mixins/default-filter.ts`
110
118
 
111
- ## FieldsVisibilityMixin
119
+ ### Abstract Requirements
112
120
 
113
- Provides hidden properties management for SQL-level field exclusion.
121
+ Classes using this mixin must implement:
114
122
 
115
- **File:** `packages/core/src/base/repositories/mixins/fields-visibility.ts`
123
+ ```typescript
124
+ abstract getEntity(): BaseEntity<TTableSchemaWithId>;
125
+ abstract get filterBuilder(): FilterBuilder;
126
+ ```
116
127
 
117
128
  ### Properties
118
129
 
119
130
  | Property | Type | Description |
120
131
  |----------|------|-------------|
121
- | `_hiddenProperties` | `Set<string> \| null` | Cached hidden property names |
122
- | `_visibleProperties` | `Record<string, any> \| null \| undefined` | Cached visible columns |
132
+ | `_defaultFilter` | `TFilter \| null \| undefined` | Cached default filter (`null` = not yet computed, `undefined` = computed with no default filter) |
123
133
 
124
134
  ### Methods
125
135
 
126
136
  | Method | Returns | Description |
127
137
  |--------|---------|-------------|
128
- | `get hiddenProperties` | `Set<string>` | Get hidden property names |
129
- | `set hiddenProperties` | `void` | Override hidden properties |
130
- | `getHiddenProperties()` | `Set<string>` | Get hidden properties from model metadata |
131
- | `hasHiddenProperties()` | `boolean` | Check if model has hidden properties |
132
- | `get visibleProperties` | `Record<string, any> \| undefined` | Get visible columns for Drizzle |
133
- | `set visibleProperties` | `void` | Override visible properties |
134
- | `getVisibleProperties()` | `Record<string, any> \| undefined` | Build visible columns object |
138
+ | `getDefaultFilter()` | `TFilter \| undefined` | Get default filter from model metadata (cached) |
139
+ | `hasDefaultFilter()` | `boolean` | Check if model has a default filter configured |
140
+ | `applyDefaultFilter(opts)` | `TFilter` | Merge default filter with user filter |
135
141
 
136
142
  ### Usage
137
143
 
138
144
  ```typescript
139
- import { FieldsVisibilityMixin } from '@venizia/ignis';
145
+ import { DefaultFilterMixin } from '@venizia/ignis';
140
146
  import { BaseHelper } from '@venizia/ignis-helpers';
141
147
 
142
- class MyRepository extends FieldsVisibilityMixin(BaseHelper) {
143
- // Required abstract implementation
148
+ class MyRepository extends DefaultFilterMixin(BaseHelper) {
149
+ // Required abstract implementations
144
150
  abstract getEntity(): BaseEntity;
151
+ abstract get filterBuilder(): FilterBuilder;
145
152
 
146
153
  // Now has access to:
147
- // - hiddenProperties (getter/setter)
148
- // - visibleProperties (getter/setter)
149
- // - getHiddenProperties()
150
- // - hasHiddenProperties()
151
- // - getVisibleProperties()
154
+ // - getDefaultFilter()
155
+ // - hasDefaultFilter()
156
+ // - applyDefaultFilter()
152
157
  }
153
158
  ```
154
159
 
155
- ### Visible Properties for Drizzle
156
-
157
- The `getVisibleProperties()` method returns a columns object for Drizzle's `select()` or `returning()`:
160
+ ### applyDefaultFilter Options
158
161
 
159
162
  ```typescript
160
- // Model with hiddenProperties: ['password', 'apiKey']
161
- // Schema columns: { id, email, password, apiKey, createdAt }
163
+ applyDefaultFilter<DataObject = any>(opts: {
164
+ userFilter?: TFilter<DataObject>; // User-provided filter
165
+ shouldSkipDefaultFilter?: boolean; // If true, bypass default filter
166
+ }): TFilter<DataObject>
167
+ ```
162
168
 
163
- const visibleProps = this.getVisibleProperties();
164
- // Result: { id: column, email: column, createdAt: column }
165
- // (password and apiKey excluded)
169
+ **Behavior:**
166
170
 
167
- // Used in Drizzle queries
168
- await connector.select(visibleProps).from(schema);
169
- // SELECT id, email, created_at FROM users
170
- ```
171
+ 1. If `shouldSkipDefaultFilter` is `true`, returns the user filter as-is (or `{}` if none).
172
+ 2. If no default filter is configured, returns the user filter as-is (or `{}` if none).
173
+ 3. Otherwise, delegates to `filterBuilder.mergeFilter({ defaultFilter, userFilter })` which deep-merges `where` conditions and uses user values for other filter properties (`order`, `limit`, `offset`, `skip`, `fields`, `include`).
171
174
 
172
175
 
173
176
  ## Mixin Composition
174
177
 
175
- The `AbstractRepository` composes multiple mixins:
178
+ The `AbstractRepository` composes both mixins:
176
179
 
177
180
  ```typescript
178
181
  export abstract class AbstractRepository<...>
@@ -180,17 +183,17 @@ export abstract class AbstractRepository<...>
180
183
  implements IPersistableRepository<...>
181
184
  {
182
185
  // Inherits from both mixins:
183
- // From DefaultFilterMixin:
184
- // - getDefaultFilter()
185
- // - hasDefaultFilter()
186
- // - applyDefaultFilter()
187
- //
188
186
  // From FieldsVisibilityMixin:
189
- // - hiddenProperties
190
- // - visibleProperties
187
+ // - hiddenProperties (getter/setter)
188
+ // - visibleProperties (getter/setter)
191
189
  // - getHiddenProperties()
192
190
  // - hasHiddenProperties()
193
191
  // - getVisibleProperties()
192
+ //
193
+ // From DefaultFilterMixin:
194
+ // - getDefaultFilter()
195
+ // - hasDefaultFilter()
196
+ // - applyDefaultFilter()
194
197
  }
195
198
  ```
196
199
 
@@ -207,7 +210,7 @@ DefaultFilterMixin(FieldsVisibilityMixin(BaseHelper))
207
210
 
208
211
  ## Creating Custom Mixins
209
212
 
210
- Follow the TypeScript mixin pattern:
213
+ Follow the TypeScript mixin pattern using `TMixinTarget`:
211
214
 
212
215
  ```typescript
213
216
  import { TMixinTarget } from '@venizia/ignis-helpers';
@@ -269,44 +272,46 @@ class ProductRepository extends AuditableRepository {
269
272
 
270
273
  ## Caching Behavior
271
274
 
272
- Both mixins use caching for performance:
275
+ Both mixins use a three-state caching pattern for performance:
273
276
 
274
277
  ```typescript
275
278
  // DefaultFilterMixin caching
276
279
  // null = not computed yet
277
- // undefined = computed, no default filter
280
+ // undefined = computed, no default filter exists
278
281
  // TFilter = computed, has default filter
279
- private _defaultFilter: TFilter | null | undefined = null;
282
+ _defaultFilter: TFilter | null | undefined = null;
280
283
 
281
284
  getDefaultFilter() {
282
285
  if (this._defaultFilter !== null) {
283
- return this._defaultFilter; // Return cached value
286
+ return this._defaultFilter; // Return cached value (either TFilter or undefined)
284
287
  }
285
- // Compute and cache...
288
+ // Compute from MetadataRegistry and cache...
286
289
  }
287
290
 
288
291
  // FieldsVisibilityMixin caching
289
292
  // null = not computed yet
290
- // Set<string> = computed
291
- private _hiddenProperties: Set<string> | null = null;
293
+ // Set<string> = computed (may be empty)
294
+ _hiddenProperties: Set<string> | null = null;
292
295
 
293
296
  // null = not computed yet
294
- // undefined = computed, no hidden properties
295
- // Record<string, any> = computed, has hidden properties
296
- private _visibleProperties: Record<string, any> | null | undefined = null;
297
+ // undefined = computed, no hidden properties exist
298
+ // Record<string, any> = computed, has visible column map
299
+ _visibleProperties: Record<string, any> | null | undefined = null;
297
300
  ```
298
301
 
302
+ This ensures metadata lookups happen only once per repository instance, with subsequent calls returning the cached value.
303
+
299
304
 
300
305
  ## Quick Reference
301
306
 
302
307
  | Mixin | Method | Purpose |
303
308
  |-------|--------|---------|
304
- | `DefaultFilterMixin` | `hasDefaultFilter()` | Check if default filter exists |
305
- | `DefaultFilterMixin` | `getDefaultFilter()` | Get raw default filter |
306
- | `DefaultFilterMixin` | `applyDefaultFilter()` | Merge filters |
307
309
  | `FieldsVisibilityMixin` | `hasHiddenProperties()` | Check if hidden props exist |
308
- | `FieldsVisibilityMixin` | `getHiddenProperties()` | Get hidden property names |
309
- | `FieldsVisibilityMixin` | `getVisibleProperties()` | Get Drizzle columns object |
310
+ | `FieldsVisibilityMixin` | `getHiddenProperties()` | Get hidden property names as `Set<string>` |
311
+ | `FieldsVisibilityMixin` | `getVisibleProperties()` | Get Drizzle columns object (excludes hidden) |
312
+ | `DefaultFilterMixin` | `hasDefaultFilter()` | Check if default filter exists |
313
+ | `DefaultFilterMixin` | `getDefaultFilter()` | Get raw default filter from model metadata |
314
+ | `DefaultFilterMixin` | `applyDefaultFilter()` | Merge default filter with user filter |
310
315
 
311
316
 
312
317
  ## Next Steps
@@ -62,6 +62,9 @@ const post = await postRepo.findOne({
62
62
  });
63
63
  ```
64
64
 
65
+ > [!NOTE]
66
+ > When `include` is present in the filter, the repository uses the **Query API** (`connector.query`) instead of the Core API. This is handled automatically by the `canUseCoreAPI` check in `ReadableRepository`.
67
+
65
68
 
66
69
  ## Scoped Includes
67
70
 
@@ -138,6 +141,23 @@ const user = await userRepo.findOne({
138
141
  });
139
142
  ```
140
143
 
144
+ ### Skip Default Filter on Includes
145
+
146
+ Each inclusion can independently bypass the related model's default filter:
147
+
148
+ ```typescript
149
+ // Include soft-deleted posts that would normally be filtered out
150
+ const user = await userRepo.findOne({
151
+ filter: {
152
+ where: { id: '123' },
153
+ include: [{
154
+ relation: 'posts',
155
+ shouldSkipDefaultFilter: true
156
+ }]
157
+ }
158
+ });
159
+ ```
160
+
141
161
 
142
162
  ## Nested Includes
143
163
 
@@ -146,7 +166,7 @@ Include relations of relations (up to 2 levels recommended):
146
166
  ### Two-Level Nesting
147
167
 
148
168
  ```typescript
149
- // User Posts Comments
169
+ // User -> Posts -> Comments
150
170
  const user = await userRepo.findOne({
151
171
  filter: {
152
172
  where: { id: '123' },
@@ -179,7 +199,7 @@ const user = await userRepo.findOne({
179
199
  ### Many-to-Many Through Junction
180
200
 
181
201
  ```typescript
182
- // Product SaleChannelProduct (junction) SaleChannel
202
+ // Product -> SaleChannelProduct (junction) -> SaleChannel
183
203
  const product = await productRepo.findOne({
184
204
  filter: {
185
205
  where: { id: 'prod1' },
@@ -216,7 +236,26 @@ const product = await productRepo.findOne({
216
236
 
217
237
  ## Defining Relations
218
238
 
219
- Relations must be defined in your model before you can `include` them.
239
+ Relations are defined using the `createRelations` helper and the `TRelationConfig` type. These use Drizzle ORM's relation system under the hood.
240
+
241
+ ### Relation Config Type
242
+
243
+ ```typescript
244
+ type TRelationConfig = {
245
+ name: string; // Relation name used in includes
246
+ } & (
247
+ | {
248
+ type: 'one'; // one-to-one or many-to-one
249
+ schema: TTableSchemaWithId;
250
+ metadata: { fields, references, relationName? };
251
+ }
252
+ | {
253
+ type: 'many'; // one-to-many
254
+ schema: TTableSchemaWithId;
255
+ metadata: { relationName? };
256
+ }
257
+ );
258
+ ```
220
259
 
221
260
  ### In Your Model
222
261
 
@@ -230,14 +269,14 @@ export const userTable = pgTable('User', {
230
269
  email: text('email').notNull(),
231
270
  });
232
271
 
233
- export const userRelations = createRelations({
272
+ const userRelationsConfig = createRelations({
234
273
  source: userTable,
235
274
  relations: [
236
275
  {
237
- type: 'hasMany',
238
- model: () => Post, // Target model
239
- foreignKey: 'authorId', // FK in Post table
240
- name: 'posts', // Relation name for includes
276
+ type: 'many',
277
+ schema: postTable,
278
+ name: 'posts',
279
+ metadata: { relationName: 'posts' },
241
280
  },
242
281
  ],
243
282
  });
@@ -245,45 +284,64 @@ export const userRelations = createRelations({
245
284
  @model({ type: 'entity' })
246
285
  export class User extends BaseEntity<typeof User.schema> {
247
286
  static override schema = userTable;
248
- static override relations = () => userRelations.definitions;
287
+ static override relations = () => userRelationsConfig.definitions;
249
288
  static override TABLE_NAME = 'User';
250
289
  }
251
290
  ```
252
291
 
253
292
  ### Relation Types
254
293
 
255
- | Type | Description | Example |
256
- |------|-------------|---------|
257
- | `hasMany` | One-to-many | User has many Posts |
258
- | `hasOne` | One-to-one | User has one Profile |
259
- | `belongsTo` | Inverse of hasMany/hasOne | Post belongs to User |
294
+ | Type | Drizzle Function | Description | Example |
295
+ |------|------------------|-------------|---------|
296
+ | `'one'` | `one()` | One-to-one or many-to-one | Post has one Author, User has one Profile |
297
+ | `'many'` | `many()` | One-to-many | User has many Posts |
260
298
 
261
- ### Example: Post Model
299
+ > [!NOTE]
300
+ > Unlike LoopBack 4's `hasMany`/`hasOne`/`belongsTo` terminology, Ignis uses Drizzle ORM's relation model which has only `one` and `many` types. A "belongsTo" relationship is expressed as `type: 'one'` with `fields` (local FK) and `references` (remote PK) in the metadata.
301
+
302
+ ### Example: Post Model with Both Types
262
303
 
263
304
  ```typescript
264
- export const postRelations = createRelations({
305
+ const postRelationsConfig = createRelations({
265
306
  source: postTable,
266
307
  relations: [
267
308
  {
268
- type: 'belongsTo',
269
- model: () => User,
270
- foreignKey: 'authorId',
309
+ type: 'one',
310
+ schema: userTable,
271
311
  name: 'author',
312
+ metadata: {
313
+ fields: [postTable.authorId],
314
+ references: [userTable.id],
315
+ },
272
316
  },
273
317
  {
274
- type: 'hasMany',
275
- model: () => Comment,
276
- foreignKey: 'postId',
318
+ type: 'many',
319
+ schema: commentTable,
277
320
  name: 'comments',
321
+ metadata: { relationName: 'comments' },
278
322
  },
279
323
  ],
280
324
  });
281
325
  ```
282
326
 
327
+ ### createRelations Return Value
328
+
329
+ `createRelations` returns an object with two properties:
330
+
331
+ ```typescript
332
+ const result = createRelations({ source, relations });
333
+
334
+ result.definitions; // Record<string, TRelationConfig> - keyed by relation name
335
+ result.relations; // Drizzle relations() call result - pass to DataSource schema
336
+ ```
337
+
338
+ - **`definitions`**: Used by `BaseEntity.relations` for include resolution at runtime.
339
+ - **`relations`**: The actual Drizzle ORM relations definition, needed for DataSource schema registration.
340
+
283
341
 
284
342
  ## Auto-Resolution
285
343
 
286
- Relations are automatically resolved from the entity's static `relations` property. No need to pass them in the repository constructor:
344
+ Relations are automatically resolved from the entity's static `relations` property via `MetadataRegistry`. The `FilterBuilder.resolveRelations()` method reads them when building include queries. No need to pass them in the repository constructor:
287
345
 
288
346
  ```typescript
289
347
  @repository({ model: User, dataSource: PostgresDataSource })
@@ -293,6 +351,27 @@ export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
293
351
  ```
294
352
 
295
353
 
354
+ ## Hidden Properties in Relations
355
+
356
+ When building include queries, the `FilterBuilder.toInclude()` method automatically:
357
+
358
+ 1. Resolves hidden properties for each related model via `resolveHiddenProperties()`.
359
+ 2. Resolves the default filter for each related model via `resolveDefaultFilter()`.
360
+ 3. Merges the default filter with any user-provided `scope`.
361
+ 4. Excludes hidden columns from the nested query's `columns` selection.
362
+
363
+ ```typescript
364
+ // User model has hiddenProperties: ['password']
365
+ const post = await postRepo.findOne({
366
+ filter: {
367
+ include: [{ relation: 'author' }]
368
+ }
369
+ });
370
+
371
+ // post.author will NOT include password - excluded at SQL level
372
+ ```
373
+
374
+
296
375
  ## Type Safety with Generics
297
376
 
298
377
  For queries with `include`, use generic type overrides for full type safety:
@@ -343,6 +422,19 @@ product?.saleChannelProducts[0].saleChannel.name;
343
422
  ```
344
423
 
345
424
 
425
+ ## TInclusion Type Reference
426
+
427
+ Each element in the `include` array has this shape:
428
+
429
+ ```typescript
430
+ type TInclusion = {
431
+ relation: string; // Name of the relation to include
432
+ scope?: TFilter; // Optional nested filter (where, order, limit, fields, include)
433
+ shouldSkipDefaultFilter?: boolean; // Skip the related model's default filter
434
+ };
435
+ ```
436
+
437
+
346
438
  ## Common Patterns
347
439
 
348
440
  ### Find All with Count of Relations
@@ -382,21 +474,6 @@ async function getUser(id: string, includePosts: boolean) {
382
474
  }
383
475
  ```
384
476
 
385
- ### Include with Hidden Properties
386
-
387
- Hidden properties (like `password`) are automatically excluded from included relations:
388
-
389
- ```typescript
390
- // User model has hiddenProperties: ['password']
391
- const post = await postRepo.findOne({
392
- filter: {
393
- include: [{ relation: 'author' }]
394
- }
395
- });
396
-
397
- // post.author will NOT include password
398
- ```
399
-
400
477
 
401
478
  ## Error Handling
402
479
 
@@ -405,7 +482,7 @@ const post = await postRepo.findOne({
405
482
  If you try to include a relation that doesn't exist:
406
483
 
407
484
  ```typescript
408
- // Error: Relation 'nonExistent' not found in User relations
485
+ // Error: [FilterBuilder][toInclude] Relation NOT FOUND | relation: 'nonExistent'
409
486
  await userRepo.find({
410
487
  filter: {
411
488
  include: [{ relation: 'nonExistent' }]
@@ -413,7 +490,15 @@ await userRepo.find({
413
490
  });
414
491
  ```
415
492
 
416
- **Fix:** Check your model's `relations` definition.
493
+ **Fix:** Check your model's `relations` definition and ensure the relation name matches.
494
+
495
+ ### Invalid Include Format
496
+
497
+ ```typescript
498
+ // Error: [FilterBuilder][toInclude] Invalid include format | include: ...
499
+ ```
500
+
501
+ **Fix:** Ensure each include element has a `relation` string property.
417
502
 
418
503
  ### Schema Key Mismatch
419
504
 
@@ -431,6 +516,7 @@ in connector.query | Available keys: [Post, Comment]
431
516
  2. **Use `fields` in scope** - Only fetch needed columns
432
517
  3. **Use `limit` in scope** - Don't fetch unbounded related data
433
518
  4. **Consider separate queries** - For complex data needs, multiple simple queries often outperform one complex nested query
519
+ 5. **Use `shouldSkipDefaultFilter` sparingly** - Only when you explicitly need filtered-out records
434
520
 
435
521
  ```typescript
436
522
  // Instead of deep nesting, use separate queries
@@ -443,7 +529,7 @@ const posts = await postRepo.find({
443
529
  });
444
530
  const comments = await commentRepo.find({
445
531
  filter: {
446
- where: { postId: { in: posts.map(p => p.id) } }
532
+ where: { postId: { inq: posts.map(p => p.id) } }
447
533
  }
448
534
  });
449
535
  ```
@@ -460,6 +546,7 @@ const comments = await commentRepo.find({
460
546
  | Limit included | `include: [{ relation: 'posts', scope: { limit: 5 } }]` |
461
547
  | Nested include | `include: [{ relation: 'posts', scope: { include: [{ relation: 'comments' }] } }]` |
462
548
  | Select fields | `include: [{ relation: 'posts', scope: { fields: ['id', 'title'] } }]` |
549
+ | Skip default filter | `include: [{ relation: 'posts', shouldSkipDefaultFilter: true }]` |
463
550
 
464
551
 
465
552
  ## Next Steps
@@ -476,11 +563,8 @@ const comments = await commentRepo.find({
476
563
 
477
564
  - **Related Topics:**
478
565
  - [Advanced Features](./advanced) - Hidden properties, transactions
479
- - [Repository Mixins](./mixins) - Soft delete and auditing
566
+ - [Repository Mixins](./mixins) - Default filter and fields visibility
480
567
  - [Filter System](/references/base/filter-system/) - Query operators
481
568
 
482
569
  - **External Resources:**
483
570
  - [Drizzle ORM Relations](https://orm.drizzle.team/docs/rqb#relations) - Relation definition guide
484
-
485
- - **Tutorials:**
486
- - [E-commerce API](/guides/tutorials/ecommerce-api) - Relations in practice