@venizia/ignis-docs 0.2.0 → 0.2.1-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 (142) hide show
  1. package/README.md +14 -14
  2. package/content/best-practices/api-usage-examples.md +39 -19
  3. package/content/best-practices/architectural-patterns.md +22 -11
  4. package/content/best-practices/architecture-decisions.md +29 -16
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
  6. package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
  7. package/content/best-practices/code-style-standards/control-flow.md +33 -1
  8. package/content/best-practices/code-style-standards/documentation.md +28 -8
  9. package/content/best-practices/code-style-standards/function-patterns.md +15 -4
  10. package/content/best-practices/code-style-standards/index.md +7 -2
  11. package/content/best-practices/code-style-standards/naming-conventions.md +26 -2
  12. package/content/best-practices/code-style-standards/route-definitions.md +9 -5
  13. package/content/best-practices/code-style-standards/tooling.md +14 -1
  14. package/content/best-practices/code-style-standards/type-safety.md +39 -6
  15. package/content/best-practices/common-pitfalls.md +59 -35
  16. package/content/best-practices/contribution-workflow.md +6 -2
  17. package/content/best-practices/data-modeling.md +78 -62
  18. package/content/best-practices/deployment-strategies.md +56 -19
  19. package/content/best-practices/error-handling.md +182 -93
  20. package/content/best-practices/index.md +6 -0
  21. package/content/best-practices/performance-optimization.md +26 -13
  22. package/content/best-practices/security-guidelines.md +35 -9
  23. package/content/best-practices/testing-strategies.md +7 -2
  24. package/content/best-practices/troubleshooting-tips.md +27 -17
  25. package/content/extensions/components/api-reference.md +107 -322
  26. package/content/extensions/components/authentication/api.md +454 -603
  27. package/content/extensions/components/authentication/errors.md +121 -498
  28. package/content/extensions/components/authentication/index.md +88 -801
  29. package/content/extensions/components/authentication/usage.md +207 -956
  30. package/content/extensions/components/authorization/api.md +736 -656
  31. package/content/extensions/components/authorization/errors.md +168 -206
  32. package/content/extensions/components/authorization/index.md +82 -797
  33. package/content/extensions/components/authorization/usage.md +194 -527
  34. package/content/extensions/components/health-check.md +71 -243
  35. package/content/extensions/components/mail/api.md +504 -287
  36. package/content/extensions/components/mail/errors.md +73 -61
  37. package/content/extensions/components/mail/index.md +96 -467
  38. package/content/extensions/components/mail/usage.md +130 -172
  39. package/content/extensions/components/request-tracker.md +66 -173
  40. package/content/extensions/components/socket-io/api.md +195 -13
  41. package/content/extensions/components/socket-io/errors.md +3 -3
  42. package/content/extensions/components/socket-io/index.md +50 -337
  43. package/content/extensions/components/socket-io/usage.md +143 -26
  44. package/content/extensions/components/static-asset/api.md +410 -142
  45. package/content/extensions/components/static-asset/errors.md +110 -53
  46. package/content/extensions/components/static-asset/index.md +79 -608
  47. package/content/extensions/components/static-asset/usage.md +180 -300
  48. package/content/extensions/components/websocket/api.md +275 -399
  49. package/content/extensions/components/websocket/errors.md +47 -56
  50. package/content/extensions/components/websocket/index.md +74 -407
  51. package/content/extensions/components/websocket/usage.md +110 -341
  52. package/content/extensions/helpers/cron/index.md +51 -160
  53. package/content/extensions/helpers/crypto/index.md +62 -483
  54. package/content/extensions/helpers/crypto/reference.md +456 -0
  55. package/content/extensions/helpers/env/index.md +60 -178
  56. package/content/extensions/helpers/error/index.md +221 -207
  57. package/content/extensions/helpers/inversion/index.md +65 -556
  58. package/content/extensions/helpers/inversion/reference.md +522 -0
  59. package/content/extensions/helpers/kafka/admin.md +20 -1
  60. package/content/extensions/helpers/kafka/compile-binary.md +41 -35
  61. package/content/extensions/helpers/kafka/consumer.md +54 -20
  62. package/content/extensions/helpers/kafka/examples.md +21 -16
  63. package/content/extensions/helpers/kafka/index.md +80 -610
  64. package/content/extensions/helpers/kafka/producer.md +134 -5
  65. package/content/extensions/helpers/kafka/schema-registry.md +45 -70
  66. package/content/extensions/helpers/logger/hf-logger.md +193 -0
  67. package/content/extensions/helpers/logger/index.md +64 -563
  68. package/content/extensions/helpers/logger/pino.md +85 -0
  69. package/content/extensions/helpers/logger/reference.md +746 -0
  70. package/content/extensions/helpers/network/api.md +241 -195
  71. package/content/extensions/helpers/network/index.md +72 -530
  72. package/content/extensions/helpers/queue/index.md +72 -900
  73. package/content/extensions/helpers/queue/reference.md +467 -0
  74. package/content/extensions/helpers/redis/index.md +73 -645
  75. package/content/extensions/helpers/redis/reference.md +727 -0
  76. package/content/extensions/helpers/secrets/index.md +66 -0
  77. package/content/extensions/helpers/socket-io/api.md +305 -203
  78. package/content/extensions/helpers/socket-io/index.md +66 -432
  79. package/content/extensions/helpers/storage/api.md +564 -462
  80. package/content/extensions/helpers/storage/index.md +77 -573
  81. package/content/extensions/helpers/types/index.md +66 -499
  82. package/content/extensions/helpers/types/reference.md +650 -0
  83. package/content/extensions/helpers/uid/index.md +58 -227
  84. package/content/extensions/helpers/websocket/api.md +329 -216
  85. package/content/extensions/helpers/websocket/index.md +65 -503
  86. package/content/extensions/helpers/worker-thread/index.md +58 -396
  87. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  88. package/content/guides/core-concepts/persistent/models.md +1 -1
  89. package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
  90. package/content/guides/core-concepts/persistent/transactions.md +1 -1
  91. package/content/guides/core-concepts/secrets-vault.md +177 -0
  92. package/content/guides/core-concepts/services.md +1 -1
  93. package/content/guides/migrations/redis-helpers-migration.md +1 -1
  94. package/content/guides/migrations/unified-connectors-migration.md +2 -2
  95. package/content/guides/tutorials/ecommerce-api.md +3 -8
  96. package/content/references/base/application.md +1 -1
  97. package/content/references/base/connectors.md +79 -136
  98. package/content/references/base/datasources-reference.md +599 -0
  99. package/content/references/base/datasources.md +84 -444
  100. package/content/references/base/dependency-injection.md +17 -39
  101. package/content/references/base/filter-system/application-usage.md +69 -121
  102. package/content/references/base/filter-system/array-operators.md +12 -0
  103. package/content/references/base/filter-system/comparison-operators.md +12 -0
  104. package/content/references/base/filter-system/default-filter.md +136 -348
  105. package/content/references/base/filter-system/fields-order-pagination.md +38 -16
  106. package/content/references/base/filter-system/index.md +106 -257
  107. package/content/references/base/filter-system/json-filtering.md +12 -2
  108. package/content/references/base/filter-system/list-operators.md +16 -2
  109. package/content/references/base/filter-system/logical-operators.md +13 -0
  110. package/content/references/base/filter-system/null-operators.md +13 -0
  111. package/content/references/base/filter-system/pattern-matching.md +12 -0
  112. package/content/references/base/filter-system/quick-reference.md +11 -2
  113. package/content/references/base/filter-system/range-operators.md +12 -0
  114. package/content/references/base/filter-system/tips.md +70 -133
  115. package/content/references/base/filter-system/use-cases.md +156 -233
  116. package/content/references/base/middlewares.md +35 -21
  117. package/content/references/base/models-reference.md +886 -0
  118. package/content/references/base/models.md +80 -1452
  119. package/content/references/base/repositories/advanced.md +156 -192
  120. package/content/references/base/repositories/index.md +77 -650
  121. package/content/references/base/repositories/mixins.md +22 -18
  122. package/content/references/base/repositories/relations.md +123 -171
  123. package/content/references/base/repositories/soft-deletable.md +58 -56
  124. package/content/references/base/secrets.md +263 -0
  125. package/content/references/base/services.md +2 -2
  126. package/content/references/configuration/environment-variables.md +48 -4
  127. package/content/references/configuration/index.md +49 -31
  128. package/content/references/quick-reference.md +3 -16
  129. package/content/references/utilities/crypto.md +35 -76
  130. package/content/references/utilities/date.md +33 -73
  131. package/content/references/utilities/index.md +1 -1
  132. package/content/references/utilities/jsx-reference.md +298 -0
  133. package/content/references/utilities/jsx.md +82 -525
  134. package/content/references/utilities/module.md +29 -62
  135. package/content/references/utilities/parse.md +34 -64
  136. package/content/references/utilities/performance.md +33 -58
  137. package/content/references/utilities/promise.md +28 -62
  138. package/content/references/utilities/request.md +57 -218
  139. package/content/references/utilities/schema.md +43 -137
  140. package/content/references/utilities/statuses-reference.md +361 -0
  141. package/content/references/utilities/statuses.md +63 -667
  142. package/package.json +8 -8
@@ -1,712 +1,139 @@
1
- # Repositories Overview
1
+ ---
2
+ title: Repositories
3
+ description: The typed data-access object for one model - CRUD, filters, and transactions without hand-written SQL
4
+ difficulty: intermediate
5
+ ---
2
6
 
3
- Repositories are the data access layer in IGNIS - they provide type-safe CRUD operations for your database entities.
7
+ # Repositories
4
8
 
5
- > [!IMPORTANT] Base vs. Connectors
6
- > `AbstractRepository` (`packages/core/src/base/repositories/core/abstract.ts`) is the engine-neutral root shared by both connectors - it declares the CRUD contract but has no Drizzle, SQL, or mixin composition. The concrete classes documented on this page (`ReadableRepository`, `PersistableRepository`, `DefaultCRUDRepository`, `SoftDeletableRepository`) belong to the **PostgreSQL connector**, built on `PostgresBaseRepository`. Typesense has its own parallel tier - see [Connectors](/references/base/connectors) and [Search & Typesense](/guides/core-concepts/persistent/search-typesense).
9
+ A repository is the typed data-access object for one model - it turns a `@model` schema into `find`, `create`, `updateById`, `deleteById`, and friends, with the query shape validated at compile time.
7
10
 
8
- **Files:** `packages/core/src/base/repositories/core/abstract.ts` (neutral) and `packages/core/src/connectors/postgres/repositories/core/*.ts` (PostgreSQL)
11
+ ## In one example
9
12
 
10
-
11
- ## Quick Start
12
-
13
- If you're new to repositories, start here:
14
-
15
- ```typescript
16
- import { DefaultCRUDRepository, repository } from '@venizia/ignis';
17
- import { Todo } from '@/models/todo.model';
18
- import { PostgresDataSource } from '@/datasources/postgres.datasource';
19
-
20
- @repository({ model: Todo, dataSource: PostgresDataSource })
21
- export class TodoRepository extends DefaultCRUDRepository<typeof Todo.schema> {
22
- // That's it! You get: find, findOne, create, updateById, deleteById, etc.
23
- }
24
- ```
25
-
26
-
27
- ## Repository Classes
28
-
29
- | Class | Capabilities | Use Case |
30
- |-------|--------------|----------|
31
- | **AbstractRepository** | Engine-neutral base class with lazy dataSource/entity resolution and @model settings getters | Extend for custom repositories |
32
- | **ReadableRepository** | Read-only operations (write methods throw errors) | Views, external tables, read-only access |
33
- | **PersistableRepository** | Read + Write operations | Full CRUD access |
34
- | **DefaultCRUDRepository** | Extends PersistableRepository (no additions) | Standard data tables (recommended) |
35
- | **SoftDeletableRepository** | CRUD + soft delete + restore | Tables with `deletedAt` column |
36
-
37
- **Most common:** Extend `DefaultCRUDRepository` for standard tables, or `SoftDeletableRepository` for soft-delete patterns.
38
-
39
- ### Hierarchy
40
-
41
- ```
42
- BaseHelper
43
- = AbstractRepository (engine-neutral abstract base, declares all CRUD signatures, src/base)
44
- |
45
- +-- PostgresBaseRepository (PostgreSQL connector; hidden-fields + default-filter logic, no mixins)
46
- |
47
- +-- ReadableRepository (implements read ops; write ops throw errors)
48
- |
49
- +-- PersistableRepository (implements write + delete ops, READ_WRITE scope)
50
- |
51
- +-- DefaultCRUDRepository (empty subclass, recommended entry point)
52
- |
53
- +-- SoftDeletableRepository (overrides delete with soft-delete)
54
- ```
55
-
56
- > [!NOTE]
57
- > Prior to the connectors restructure, `AbstractRepository` composed `FieldsVisibilityMixin`/`DefaultFilterMixin` directly onto `BaseHelper`. Those mixins have been **removed** - see [Repository Mixins](./mixins) - and the equivalent behavior lives in `AbstractRepository`'s settings getters and `PostgresBaseRepository` instead.
58
-
59
- ### Type Parameters
60
-
61
- All repository classes share the same four type parameters:
62
-
63
- ```typescript
64
- class DefaultCRUDRepository<
65
- EntitySchema extends TTableSchemaWithId = TTableSchemaWithId,
66
- DataObject extends TTableObject<EntitySchema> = TTableObject<EntitySchema>,
67
- PersistObject extends TTableInsert<EntitySchema> = TTableInsert<EntitySchema>,
68
- ExtraOptions extends IExtraOptions = IDatabaseExtraOptions,
69
- >
70
- ```
71
-
72
- | Parameter | Description |
73
- |-----------|-------------|
74
- | `EntitySchema` | The Drizzle `pgTable` schema type (e.g., `typeof User.schema`) |
75
- | `DataObject` | The inferred SELECT type from the schema |
76
- | `PersistObject` | The inferred INSERT type from the schema |
77
- | `ExtraOptions` | Extra options for operations (defaults to `IDatabaseExtraOptions`, which narrows `IExtraOptions.transaction` to `IDatabaseTransaction`) |
78
-
79
-
80
- ## Available Methods
81
-
82
- ### Read Operations
83
- | Method | Description | Example |
84
- |--------|-------------|---------|
85
- | `find(opts)` | Find multiple records | `repository.find({ filter: { where: { status: 'active' } } })` |
86
- | `find(opts)` with range | Find with pagination range | `repository.find({ filter, options: { shouldQueryRange: true } })` |
87
- | `findOne(opts)` | Find single record | `repository.findOne({ filter: { where: { email } } })` |
88
- | `findById(opts)` | Find by primary key | `repository.findById({ id: '123' })` |
89
- | `count(opts)` | Count matching records | `repository.count({ where: { status: 'active' } })` |
90
- | `existsWith(opts)` | Check if exists | `repository.existsWith({ where: { email } })` |
91
-
92
- ### Write Operations
93
- | Method | Description | Example |
94
- |--------|-------------|---------|
95
- | `create(opts)` | Create single record | `repository.create({ data: { title: 'New' } })` |
96
- | `createAll(opts)` | Create multiple records | `repository.createAll({ data: [{ title: 'A' }, { title: 'B' }] })` |
97
- | `updateById(opts)` | Update by primary key | `repository.updateById({ id: '123', data: { title: 'Updated' } })` |
98
- | `updateAll(opts)` | Update matching records | `repository.updateAll({ data: { status: 'published' }, where: { status: 'draft' } })` |
99
- | `updateBy(opts)` | Alias for `updateAll` | `repository.updateBy({ data: { status: 'published' }, where: { status: 'draft' } })` |
100
- | `deleteById(opts)` | Delete by primary key | `repository.deleteById({ id: '123' })` |
101
- | `deleteAll(opts)` | Delete matching records | `repository.deleteAll({ where: { status: 'archived' } })` |
102
- | `deleteBy(opts)` | Alias for `deleteAll` | `repository.deleteBy({ where: { status: 'archived' } })` |
103
-
104
-
105
- ## Method Signatures
106
-
107
- ### Read Operations
108
-
109
- ```typescript
110
- // Count matching records
111
- count(opts: {
112
- where: TWhere<DataObject>;
113
- options?: IExtraOptions;
114
- }): Promise<{ count: number }>;
115
-
116
- // Check if any record matches
117
- existsWith(opts: {
118
- where: TWhere<DataObject>;
119
- options?: IExtraOptions;
120
- }): Promise<boolean>;
121
-
122
- // Find multiple records (returns array)
123
- find<R = DataObject>(opts: {
124
- filter: TFilter<DataObject>;
125
- options?: IExtraOptions & { shouldQueryRange?: false };
126
- }): Promise<R[]>;
127
-
128
- // Find multiple records with range info (returns data + range)
129
- find<R = DataObject>(opts: {
130
- filter: TFilter<DataObject>;
131
- options: IExtraOptions & { shouldQueryRange: true };
132
- }): Promise<{ data: Array<R>; range: TDataRange }>;
133
-
134
- // Find single record
135
- findOne<R = DataObject>(opts: {
136
- filter: TFilter<DataObject>;
137
- options?: IExtraOptions;
138
- }): Promise<R | null>;
139
-
140
- // Find by primary key
141
- findById<R = DataObject>(opts: {
142
- id: IdType;
143
- filter?: Omit<TFilter<DataObject>, 'where'>;
144
- options?: IExtraOptions;
145
- }): Promise<R | null>;
146
- ```
147
-
148
- ### Write Operations
149
-
150
- ```typescript
151
- // Create single record (returns created data by default)
152
- create<R = DataObject>(opts: {
153
- data: PersistObject;
154
- options?: IExtraOptions & { shouldReturn?: true };
155
- }): Promise<{ count: number; data: R }>;
156
-
157
- // Create single record (skip returning data)
158
- create(opts: {
159
- data: PersistObject;
160
- options: IExtraOptions & { shouldReturn: false };
161
- }): Promise<{ count: number; data: undefined | null }>;
162
-
163
- // Create multiple records (returns created data by default)
164
- createAll<R = DataObject>(opts: {
165
- data: Array<PersistObject>;
166
- options?: IExtraOptions & { shouldReturn?: true };
167
- }): Promise<{ count: number; data: Array<R> }>;
168
-
169
- // Create multiple records (skip returning data)
170
- createAll(opts: {
171
- data: Array<PersistObject>;
172
- options: IExtraOptions & { shouldReturn: false };
173
- }): Promise<{ count: number; data: undefined | null }>;
174
-
175
- // Update by primary key (returns updated data by default)
176
- updateById<R = DataObject>(opts: {
177
- id: IdType;
178
- data: Partial<PersistObject>;
179
- options?: IExtraOptions & { shouldReturn?: true };
180
- }): Promise<{ count: number; data: R }>;
181
-
182
- // Update by primary key (skip returning data)
183
- updateById(opts: {
184
- id: IdType;
185
- data: Partial<PersistObject>;
186
- options: IExtraOptions & { shouldReturn: false };
187
- }): Promise<{ count: number; data: undefined | null }>;
188
-
189
- // Update matching records (returns updated data by default)
190
- updateAll<R = DataObject>(opts: {
191
- data: Partial<PersistObject>;
192
- where: TWhere<DataObject>;
193
- options?: IExtraOptions & { shouldReturn?: true; force?: boolean };
194
- }): Promise<{ count: number; data: Array<R> }>;
195
-
196
- // Update matching records (skip returning data)
197
- updateAll(opts: {
198
- data: Partial<PersistObject>;
199
- where: TWhere<DataObject>;
200
- options: IExtraOptions & { shouldReturn: false; force?: boolean };
201
- }): Promise<{ count: number; data: undefined | null }>;
202
-
203
- // updateBy is an alias for updateAll (same signatures)
204
-
205
- // Delete by primary key (returns deleted data by default)
206
- deleteById<R = DataObject>(opts: {
207
- id: IdType;
208
- options?: IExtraOptions & { shouldReturn?: true };
209
- }): Promise<{ count: number; data: R }>;
210
-
211
- // Delete by primary key (skip returning data)
212
- deleteById(opts: {
213
- id: IdType;
214
- options: IExtraOptions & { shouldReturn: false };
215
- }): Promise<{ count: number; data: undefined | null }>;
216
-
217
- // Delete matching records (returns deleted data by default)
218
- deleteAll<R = DataObject>(opts: {
219
- where: TWhere<DataObject>;
220
- options?: IExtraOptions & { shouldReturn?: true; force?: boolean };
221
- }): Promise<{ count: number; data: Array<R> }>;
222
-
223
- // Delete matching records (skip returning data)
224
- deleteAll(opts: {
225
- where: TWhere<DataObject>;
226
- options: IExtraOptions & { shouldReturn: false; force?: boolean };
227
- }): Promise<{ count: number; data: undefined | null }>;
228
-
229
- // deleteBy is an alias for deleteAll (same signatures)
230
- ```
231
-
232
-
233
- ## IExtraOptions
234
-
235
- All repository operations accept an `options` parameter with these fields:
236
-
237
- ```typescript
238
- interface IExtraOptions {
239
- /** Transaction context - switches the underlying Drizzle connector. */
240
- transaction?: ITransaction;
241
-
242
- /** Operation logging configuration. */
243
- log?: { use: boolean; level?: TLogLevel };
244
-
245
- /** If true, bypass the default filter configured in model settings (e.g., soft delete). */
246
- shouldSkipDefaultFilter?: boolean;
247
-
248
- /** Row-level locking (requires transaction, incompatible with Query API). */
249
- lock?: TLockOptions;
250
- }
251
- ```
252
-
253
- > [!NOTE]
254
- > Postgres narrows this via `IDatabaseExtraOptions` (`connectors/postgres/repositories/common`), which overrides `transaction` to `IDatabaseTransaction` so `options.transaction.connector` needs no cast.
255
-
256
- Additional fields are available as intersections on specific methods:
257
-
258
- | Field | Type | Methods | Description |
259
- |-------|------|---------|-------------|
260
- | `shouldReturn` | `boolean` | `create`, `createAll`, `updateById`, `updateAll`, `deleteById`, `deleteAll` | If `false`, skip returning the data (only return count). Defaults to `true`. |
261
- | `shouldQueryRange` | `boolean` | `find` | If `true`, returns `{ data, range: { start, end, total } }` instead of a plain array. |
262
- | `force` | `boolean` | `updateAll`, `deleteAll`, `updateBy`, `deleteBy` | Required to allow empty `where` conditions. |
263
-
264
-
265
- ## TDataRange
266
-
267
- When `shouldQueryRange: true` is used, the range follows the HTTP Content-Range standard:
268
-
269
- ```typescript
270
- type TDataRange = {
271
- start: number; // Inclusive start index (based on skip/offset)
272
- end: number; // Inclusive end index
273
- total: number; // Total matching records (ignoring limit)
274
- };
275
- ```
276
-
277
-
278
- ## AbstractRepository / PostgresBaseRepository Properties
279
-
280
- ### dataSource
281
-
282
- Getter/setter for the repository's datasource. Throws if accessed before being set (either via constructor or `@repository` auto-injection).
283
-
284
- ```typescript
285
- // AbstractRepository (engine-neutral, src/base)
286
- get dataSource(): AbstractDataSource;
287
- set dataSource(value: AbstractDataSource);
288
- setDataSource(opts: { dataSource: AbstractDataSource }): void;
289
-
290
- // PostgresBaseRepository narrows the return type
291
- get dataSource(): IPostgresDataSource;
292
- set dataSource(value: IPostgresDataSource);
293
- ```
294
-
295
- ### entity
296
-
297
- Lazy-resolved from `@repository` metadata on first access. Can also be set explicitly via constructor `entityClass` option.
298
-
299
- ```typescript
300
- // AbstractRepository
301
- get entity(): AbstractEntity;
302
- set entity(value: AbstractEntity);
303
- getEntity(): AbstractEntity;
304
-
305
- // PostgresBaseRepository narrows the return type
306
- get entity(): BasePostgresEntity<EntitySchema>;
307
- set entity(value: BasePostgresEntity<EntitySchema>);
308
- getEntitySchema(): EntitySchema;
309
- ```
310
-
311
- `BaseEntity` is a compatibility alias re-exported for `BasePostgresEntity` - prefer `BasePostgresEntity` in new code.
312
-
313
- ### operationScope
314
-
315
- Returns the repository's operation scope: `'READ_ONLY'`, `'WRITE_ONLY'`, or `'READ_WRITE'`.
316
-
317
- ```typescript
318
- get operationScope(): TRepositoryOperationScope;
319
- ```
320
-
321
- - `ReadableRepository` defaults to `READ_ONLY`
322
- - `PersistableRepository` and `DefaultCRUDRepository` default to `READ_WRITE`
323
-
324
- ### filterBuilder (PostgresBaseRepository+)
325
-
326
- Access to the `FilterBuilder` instance used for converting filter objects to Drizzle SQL. Not present on the engine-neutral `AbstractRepository`.
13
+ The smallest real repository: bind a model and a datasource, extend `DefaultCRUDRepository`.
327
14
 
328
15
  ```typescript
329
- get filterBuilder(): FilterBuilder;
330
- ```
331
-
332
- ### connector (PostgresBaseRepository+)
16
+ import { repository, DefaultCRUDRepository } from '@venizia/ignis';
17
+ import { User } from '../models/user.model';
18
+ import { PostgresDataSource } from '../datasources/postgres.datasource';
333
19
 
334
- Shortcut for `this.dataSource.connector`. Not present on the engine-neutral `AbstractRepository`.
335
-
336
- ```typescript
337
- get connector(): IPostgresDataSource['connector'];
338
- getConnector(): IPostgresDataSource['connector'];
339
- ```
340
-
341
- ### updateBuilder (PersistableRepository+)
342
-
343
- Access to the `UpdateBuilder` instance used for transforming update data (including JSON path updates).
344
-
345
- ```typescript
346
- get updateBuilder(): UpdateBuilder;
347
- ```
348
-
349
-
350
- ## Key Methods
351
-
352
- ### beginTransaction (PostgresBaseRepository+)
353
-
354
- Delegates to the repository's datasource. Not present on the engine-neutral `AbstractRepository` - each connector adds it with its own transaction type (or omits it, like typesense).
355
-
356
- ```typescript
357
- beginTransaction(opts?: IDatabaseTransactionOptions): Promise<IDatabaseTransaction>;
358
- ```
359
-
360
- Usage:
361
-
362
- ```typescript
363
- const tx = await repository.beginTransaction();
364
- try {
365
- await repository.create({ data: { name: 'John' }, options: { transaction: tx } });
366
- await repository.updateById({ id: '456', data: { count: 1 }, options: { transaction: tx } });
367
- await tx.commit();
368
- } catch (e) {
369
- await tx.rollback();
370
- }
20
+ @repository({ model: User, dataSource: PostgresDataSource })
21
+ export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
371
22
  ```
372
23
 
373
- ### buildQuery
24
+ That's it - `UserRepository` already has `find`, `findOne`, `findById`, `create`, `createAll`, `updateById`, `updateAll`, `deleteById`, `deleteAll`, `count`, and `existsWith`.
374
25
 
375
- Converts a `TFilter` into Drizzle query options, automatically excluding hidden properties from `@model` settings:
26
+ ## How it works
376
27
 
377
- ```typescript
378
- buildQuery(opts: { filter: TFilter<DataObject> }): TDrizzleQueryOptions;
379
- ```
28
+ - **Engine-neutral contract, PostgreSQL implementation.** `AbstractRepository` (engine-neutral, `src/base`) declares the CRUD contract - no SQL, no Drizzle. The PostgreSQL connector implements it as a chain of classes, each layer adding one capability (see table below).
29
+ - **Datasource is auto-injected.** `@repository({ model, dataSource })` auto-injects the datasource at constructor param[0] and lazily resolves the entity class from its own metadata - a plain `extends DefaultCRUDRepository<...> {}` needs no constructor at all.
30
+ - **One options object per verb.** Reads and updates carry a `filter` (`where`, `fields`, `include`, `order`, `limit`, `offset`); writes carry `data`; all of them accept an `options` bag for `transaction`, `shouldReturn`, and `shouldSkipDefaultFilter`.
380
31
 
381
- The returned `TDrizzleQueryOptions` contains:
32
+ **PostgreSQL class chain**
382
33
 
383
- ```typescript
384
- type TDrizzleQueryOptions = Partial<{
385
- limit: number;
386
- offset: number;
387
- orderBy: SQL[];
388
- where: SQL;
389
- with: Record<string, true | TDrizzleQueryOptions>;
390
- columns: Record<string, boolean>;
391
- }>;
392
- ```
34
+ | Class | Alias | Adds |
35
+ |---|---|---|
36
+ | `RelationalBaseRepository` | `PostgresBaseRepository` | `FilterBuilder`/`UpdateBuilder`, hidden-column exclusion |
37
+ | `ReadableRelationalRepository` | `ReadableRepository` | The read verbs |
38
+ | `PersistableRelationalRepository` | `PersistableRepository` | create/update/delete |
39
+ | `DefaultRelationalRepository` | `DefaultCRUDRepository` | Empty - the recommended entry point |
40
+ | `SoftDeletableRelationalRepository` | `SoftDeletableRepository` | Overrides delete to set `deletedAt` instead of removing the row |
393
41
 
42
+ ## Common tasks
394
43
 
395
- ## Dual Query API
44
+ ### Read with a filter
396
45
 
397
- `ReadableRepository` automatically selects the optimal query strategy:
398
-
399
- - **Core API** (Drizzle `select().from()`): ~15-20% faster for flat queries without relations or field selection
400
- - **Query API** (Drizzle `connector.query[entity].findMany()`): Supports `include` for relations and `fields` for column selection
401
-
402
- The selection is automatic based on filter complexity:
46
+ `find` and `findOne` take `filter.where`, plus `order` and `limit` for paging.
403
47
 
404
48
  ```typescript
405
- // Uses Core API (no include, no fields)
406
- await repository.find({ filter: { where: { status: 'active' }, limit: 10 } });
407
-
408
- // Uses Query API (has include)
409
- await repository.find({
49
+ const users = await userRepository.find({
410
50
  filter: {
411
51
  where: { status: 'active' },
412
- include: [{ relation: 'posts' }],
413
- },
414
- });
415
-
416
- // Uses Query API (has fields)
417
- await repository.find({
418
- filter: {
419
- fields: { id: true, name: true },
420
- where: { status: 'active' },
52
+ order: ['createdAt DESC'],
53
+ limit: 20,
421
54
  },
422
55
  });
423
56
  ```
424
57
 
58
+ See [Filter System](/references/base/filter-system/) for every operator (`gte`, `like`, `inq`, JSON paths, `and`/`or`).
425
59
 
426
- ## Constructor
427
-
428
- ### AbstractRepository
429
-
430
- ```typescript
431
- constructor(
432
- dataSource?: AbstractDataSource,
433
- opts?: {
434
- scope?: string;
435
- entityClass?: TClass<AbstractEntity>;
436
- operationScope?: TRepositoryOperationScope;
437
- },
438
- )
439
- ```
440
-
441
- - `dataSource` -- DataSource instance (optional; auto-injected by `@repository` decorator)
442
- - `opts.scope` -- Logger scope name (defaults to class name)
443
- - `opts.entityClass` -- Entity class to instantiate (optional; lazy-resolved from `@repository` metadata)
444
- - `opts.operationScope` -- Defaults to `READ_ONLY`
445
-
446
- ### ReadableRepository (PostgreSQL)
447
-
448
- ```typescript
449
- constructor(
450
- ds?: IPostgresDataSource,
451
- opts?: { entityClass?: TClass<BasePostgresEntity<EntitySchema>> },
452
- )
453
- ```
454
-
455
- Forces `operationScope` to `READ_ONLY`.
60
+ ### Create a record
456
61
 
457
- ### PersistableRepository (PostgreSQL)
62
+ `create` returns `{ count, data }`, not the bare record - `count` is `1` on success, `data` is the inserted row (or `null` if `options.shouldReturn: false`).
458
63
 
459
64
  ```typescript
460
- constructor(
461
- ds?: IPostgresDataSource,
462
- opts?: { entityClass?: TClass<BasePostgresEntity<EntitySchema>> },
463
- )
65
+ const { count, data } = await userRepository.create({
66
+ data: { email: 'jane@example.com' },
67
+ });
464
68
  ```
465
69
 
466
- Forces `operationScope` to `READ_WRITE`. Also creates an `UpdateBuilder` instance.
467
-
468
-
469
- ## Documentation Sections
70
+ ### Update by id
470
71
 
471
- This documentation is split into focused guides:
472
-
473
- ### [Filter System](/references/base/filter-system/)
474
- Complete reference for querying data - operators, JSON filtering, array operators, default filters, and query patterns.
72
+ `updateById` returns the same `{ count, data }` shape, with `data` set to the updated row.
475
73
 
476
74
  ```typescript
477
- // Preview
478
- await repository.find({
479
- filter: {
480
- where: {
481
- status: 'active',
482
- age: { gte: 18 },
483
- 'metadata.priority': { gte: 3 },
484
- tags: { contains: ['featured'] }
485
- },
486
- order: ['createdAt DESC'],
487
- limit: 20
488
- }
75
+ const { data: updated } = await userRepository.updateById({
76
+ id: '123',
77
+ data: { email: 'new@example.com' },
489
78
  });
490
79
  ```
491
80
 
492
- ### [Relations & Includes](./relations.md)
493
- Fetch related data using `include` for eager loading and nested queries.
81
+ ### Soft delete
494
82
 
495
- ```typescript
496
- // Preview
497
- await repository.find({
498
- filter: {
499
- include: [{
500
- relation: 'posts',
501
- scope: { where: { published: true } }
502
- }]
503
- }
504
- });
505
- ```
506
-
507
- ### [SoftDeletableRepository](./soft-deletable.md)
508
- Soft-delete and restore operations using `deletedAt` timestamps instead of physical deletion.
83
+ Extend `SoftDeletableRepository` instead of `DefaultCRUDRepository` on a model with a `deletedAt` column - `deleteById` sets the timestamp instead of removing the row, and `restoreById` clears it.
509
84
 
510
85
  ```typescript
511
- // Preview
512
86
  @repository({ model: Category, dataSource: PostgresDataSource })
513
87
  export class CategoryRepository extends SoftDeletableRepository<typeof Category.schema> {}
514
88
 
515
- // Soft delete (sets deletedAt)
516
- await repository.deleteById({ id: '123' });
517
- // Restore
518
- await repository.restoreById({ id: '123' });
519
- // Hard delete (physical removal)
520
- await repository.deleteById({ id: '123', options: { shouldHardDelete: true } });
89
+ await categoryRepository.deleteById({ id: '123' }); // sets deletedAt
90
+ await categoryRepository.restoreById({ id: '123' }); // clears deletedAt
521
91
  ```
522
92
 
523
- ### [Advanced Features](./advanced.md)
524
- Transactions, hidden properties, default filter bypass, performance optimization, and type inference.
525
-
526
- ```typescript
527
- // Preview
528
- const tx = await repository.beginTransaction();
529
- try {
530
- await repository.create({ data, options: { transaction: tx } });
531
- await tx.commit();
532
- } catch (e) {
533
- await tx.rollback();
534
- }
535
- ```
536
-
537
- ### [Repository Mixins (Removed)](./mixins.md)
538
- Tombstone for the removed `DefaultFilterMixin` and `FieldsVisibilityMixin` - where the equivalent behavior lives now.
93
+ See [SoftDeletableRepository](./soft-deletable) for hard delete and bulk restore.
539
94
 
95
+ ### Include relations
540
96
 
541
- ## @repository Decorator
542
-
543
- **Both `model` AND `dataSource` are required** for schema auto-discovery:
97
+ Pass `include` in the filter to eager-load related rows, with an optional nested `scope` filter.
544
98
 
545
99
  ```typescript
546
- @repository({ model: Model, dataSource: DataSourceClass })
100
+ await userRepository.find({
101
+ filter: {
102
+ include: [{ relation: 'posts', scope: { where: { published: true } } }],
103
+ },
104
+ });
547
105
  ```
548
106
 
549
- The decorator accepts `IRepositoryMetadata`:
107
+ See [Relations & Includes](./relations) for one-to-many, many-to-many, and nested includes.
550
108
 
551
- ```typescript
552
- interface IRepositoryMetadata<Schema, Model, DataSource> {
553
- model: TValueOrResolver<TClass<Model>>;
554
- dataSource: string | TValueOrResolver<TClass<DataSource>>;
555
- operationScope?: TRepositoryOperationScope; // 'READ_ONLY' | 'WRITE_ONLY' | 'READ_WRITE'
556
- }
557
- ```
109
+ ### Run inside a transaction
558
110
 
559
- ```typescript
560
- // WRONG - Missing dataSource
561
- @repository({ model: User })
562
- export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
563
-
564
- // WRONG - Missing model
565
- @repository({ dataSource: PostgresDataSource })
566
- export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
567
-
568
- // CORRECT
569
- @repository({ model: User, dataSource: PostgresDataSource })
570
- export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
571
- ```
572
-
573
- ### Zero Boilerplate Pattern (Recommended)
574
-
575
- DataSource is auto-injected - no constructor needed:
111
+ `beginTransaction()` delegates to the datasource; pass the handle as `options.transaction` on any repository call to run it inside that transaction.
576
112
 
577
113
  ```typescript
578
- @repository({ model: User, dataSource: PostgresDataSource })
579
- export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
580
- // Custom methods only - no boilerplate!
114
+ const transaction = await userRepository.beginTransaction();
581
115
 
582
- async findByEmail(opts: { email: string }) {
583
- return this.findOne({ filter: { where: { email: opts.email } } });
584
- }
585
- }
586
- ```
587
-
588
- ### Explicit @inject Pattern
589
-
590
- When you need constructor control:
591
-
592
- ```typescript
593
- @repository({ model: User, dataSource: PostgresDataSource })
594
- export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
595
- constructor(
596
- @inject({ key: 'datasources.PostgresDataSource' })
597
- dataSource: PostgresDataSource,
598
- ) {
599
- super(dataSource);
600
- }
116
+ try {
117
+ await userRepository.create({ data: { email: 'a@example.com' }, options: { transaction } });
118
+ await transaction.commit();
119
+ } catch (error) {
120
+ await transaction.rollback();
121
+ throw error;
601
122
  }
602
123
  ```
603
124
 
604
- ### Lazy Resolution
605
-
606
- The `@repository` decorator enables two lazy resolution mechanisms:
607
-
608
- 1. **Entity resolution**: The `entity` getter auto-resolves the model class from `@repository` metadata on first access, so you never need to pass `entityClass` manually.
609
- 2. **DataSource resolution**: The DataSource is auto-injected at constructor param[0] unless an explicit `@inject` is present.
610
-
611
-
612
- ## Safety Features
613
-
614
- ### Empty Where Protection
615
-
616
- Prevents accidental mass updates/deletes (in `PersistableRepository` and above):
617
-
618
- ```typescript
619
- // Throws error - empty where without force flag
620
- await repository.deleteAll({ where: {} });
621
- await repository.updateAll({ data: { status: 'archived' }, where: {} });
622
-
623
- // Explicitly allow with force flag (logs warning)
624
- await repository.deleteAll({ where: {}, options: { force: true } });
625
- await repository.updateAll({ data: { status: 'archived' }, where: {}, options: { force: true } });
626
- ```
627
-
628
- | Scenario | `force: false` (default) | `force: true` |
629
- |----------|-------------------------|---------------|
630
- | Empty `where` | Throws error | Logs warning, proceeds |
631
- | Valid `where` | Executes normally | Executes normally |
125
+ See [DataSources](/references/base/datasources) for the rollback-safe pattern (`rollback()` itself can throw). See [Advanced Features](./advanced) for isolation levels and other transaction options.
632
126
 
633
- ### ReadableRepository Write Protection
127
+ ## See also
634
128
 
635
- All write methods (`create`, `createAll`, `updateById`, `updateAll`, `deleteById`, `deleteAll`) throw errors on `ReadableRepository`, enforcing the read-only scope at runtime. The thrown error carries `messageCode: 'core.repository.operation_not_allowed'` (`RepositoryErrorCodes.OPERATION_NOT_ALLOWED`).
636
-
637
-
638
- ## TFilter Reference
639
-
640
- ```typescript
641
- type TFilter<T = any> = {
642
- where?: TWhere<T>;
643
- fields?: Partial<{ [K in keyof T]: boolean }> | Array<keyof T>;
644
- include?: Array<{
645
- relation: string;
646
- scope?: TFilter;
647
- shouldSkipDefaultFilter?: boolean;
648
- }>;
649
- order?: string[]; // e.g., ['createdAt DESC', 'name ASC']
650
- limit?: number; // Defaults to 10
651
- offset?: number;
652
- skip?: number; // Alias for offset
653
- };
654
- ```
129
+ - [Relations & Includes](./relations) - eager loading, nested `scope` filters, many-to-many
130
+ - [Advanced Features](./advanced) - transactions, hidden properties, `shouldQueryRange`, performance
131
+ - [SoftDeletableRepository](./soft-deletable) - soft delete, restore, hard delete
132
+ - [Repository Mixins (Removed)](./mixins) - where `FieldsVisibilityMixin`/`DefaultFilterMixin` behavior lives now
133
+ - [Filter System](/references/base/filter-system/) - every `where` operator, ordering, pagination
134
+ - [Repositories Guide](/guides/core-concepts/persistent/repositories) - creating repositories step by step
655
135
 
136
+ **Files:**
656
137
 
657
- ## Quick Reference
658
-
659
- | Want to... | Code |
660
- |------------|------|
661
- | Find all active | `repository.find({ filter: { where: { status: 'active' } } })` |
662
- | Find with range info | `repository.find({ filter, options: { shouldQueryRange: true } })` |
663
- | Find by ID | `repository.findById({ id: '123' })` |
664
- | Find with relations | `repository.find({ filter: { include: [{ relation: 'posts' }] } })` |
665
- | Create one | `repository.create({ data: { name: 'John' } })` |
666
- | Create without returning data | `repository.create({ data: { name: 'John' }, options: { shouldReturn: false } })` |
667
- | Create many | `repository.createAll({ data: [{ name: 'A' }, { name: 'B' }] })` |
668
- | Update by ID | `repository.updateById({ id: '123', data: { name: 'Jane' } })` |
669
- | Update by condition | `repository.updateAll({ data: { status: 'published' }, where: { status: 'draft' } })` |
670
- | Delete by ID | `repository.deleteById({ id: '123' })` |
671
- | Delete by condition | `repository.deleteBy({ where: { status: 'archived' } })` |
672
- | Soft delete | `repository.deleteById({ id: '123' })` (with `SoftDeletableRepository`) |
673
- | Restore soft-deleted | `repository.restoreById({ id: '123' })` (with `SoftDeletableRepository`) |
674
- | Hard delete (bypass soft) | `repository.deleteById({ id: '123', options: { shouldHardDelete: true } })` |
675
- | Count matching | `repository.count({ where: { status: 'active' } })` |
676
- | Check exists | `repository.existsWith({ where: { email: 'test@example.com' } })` |
677
- | Skip default filter | `repository.find({ filter, options: { shouldSkipDefaultFilter: true } })` |
678
- | Use transaction | `repository.create({ data, options: { transaction: tx } })` |
679
-
680
-
681
- ## Next Steps
682
-
683
- - **New to filtering?** Start with [Filter System](/references/base/filter-system/)
684
- - **Need related data?** See [Relations & Includes](./relations.md)
685
- - **Need soft delete?** See [SoftDeletableRepository](./soft-deletable.md)
686
- - **Need transactions?** Go to [Advanced Features](./advanced.md)
687
-
688
- ## See Also
689
-
690
- - **Related Concepts:**
691
- - [Repositories Guide](/guides/core-concepts/persistent/repositories) - Creating repositories tutorial
692
- - [Models](/guides/core-concepts/persistent/models) - Entity definitions used by repositories
693
- - [DataSources](/guides/core-concepts/persistent/datasources) - Database connections
694
- - [Services](/guides/core-concepts/services) - Use repositories for data access
695
- - [Transactions](/guides/core-concepts/persistent/transactions) - Multi-operation consistency
696
-
697
- - **Repository Topics:**
698
- - [Relations & Includes](./relations) - Loading related data
699
- - [Advanced Features](./advanced) - JSON updates, transactions, performance tuning
700
- - [Repository Mixins (Removed)](./mixins) - Where mixin behavior lives now
701
-
702
- - **Filtering:**
703
- - [Filter System Overview](/references/base/filter-system/) - Complete filtering guide
704
- - [Filter Quick Reference](/references/base/filter-system/quick-reference) - All operators cheat sheet
705
-
706
- - **Best Practices:**
707
- - [Data Modeling](/best-practices/data-modeling) - Repository design patterns
708
- - [Performance Optimization](/best-practices/performance-optimization) - Query optimization
709
-
710
- - **Tutorials:**
711
- - [Building a CRUD API](/guides/tutorials/building-a-crud-api) - Repository examples
712
- - [E-commerce API](/guides/tutorials/ecommerce-api) - Advanced queries and relations
138
+ - [`packages/core/src/base/repositories/core/abstract.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/core/abstract.ts) - neutral `AbstractRepository`
139
+ - [`packages/core/src/connectors/postgres/repositories/core/index.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/core/index.ts) - PostgreSQL hierarchy + compatibility aliases