@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.
- package/README.md +14 -14
- package/content/best-practices/api-usage-examples.md +39 -19
- package/content/best-practices/architectural-patterns.md +22 -11
- package/content/best-practices/architecture-decisions.md +29 -16
- package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
- package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
- package/content/best-practices/code-style-standards/control-flow.md +33 -1
- package/content/best-practices/code-style-standards/documentation.md +28 -8
- package/content/best-practices/code-style-standards/function-patterns.md +15 -4
- package/content/best-practices/code-style-standards/index.md +7 -2
- package/content/best-practices/code-style-standards/naming-conventions.md +26 -2
- package/content/best-practices/code-style-standards/route-definitions.md +9 -5
- package/content/best-practices/code-style-standards/tooling.md +14 -1
- package/content/best-practices/code-style-standards/type-safety.md +39 -6
- package/content/best-practices/common-pitfalls.md +59 -35
- package/content/best-practices/contribution-workflow.md +6 -2
- package/content/best-practices/data-modeling.md +78 -62
- package/content/best-practices/deployment-strategies.md +56 -19
- package/content/best-practices/error-handling.md +182 -93
- package/content/best-practices/index.md +6 -0
- package/content/best-practices/performance-optimization.md +26 -13
- package/content/best-practices/security-guidelines.md +35 -9
- package/content/best-practices/testing-strategies.md +7 -2
- package/content/best-practices/troubleshooting-tips.md +27 -17
- package/content/extensions/components/api-reference.md +107 -322
- package/content/extensions/components/authentication/api.md +454 -603
- package/content/extensions/components/authentication/errors.md +121 -498
- package/content/extensions/components/authentication/index.md +88 -801
- package/content/extensions/components/authentication/usage.md +207 -956
- package/content/extensions/components/authorization/api.md +736 -656
- package/content/extensions/components/authorization/errors.md +168 -206
- package/content/extensions/components/authorization/index.md +82 -797
- package/content/extensions/components/authorization/usage.md +194 -527
- package/content/extensions/components/health-check.md +71 -243
- package/content/extensions/components/mail/api.md +504 -287
- package/content/extensions/components/mail/errors.md +73 -61
- package/content/extensions/components/mail/index.md +96 -467
- package/content/extensions/components/mail/usage.md +130 -172
- package/content/extensions/components/request-tracker.md +66 -173
- package/content/extensions/components/socket-io/api.md +195 -13
- package/content/extensions/components/socket-io/errors.md +3 -3
- package/content/extensions/components/socket-io/index.md +50 -337
- package/content/extensions/components/socket-io/usage.md +143 -26
- package/content/extensions/components/static-asset/api.md +410 -142
- package/content/extensions/components/static-asset/errors.md +110 -53
- package/content/extensions/components/static-asset/index.md +79 -608
- package/content/extensions/components/static-asset/usage.md +180 -300
- package/content/extensions/components/websocket/api.md +275 -399
- package/content/extensions/components/websocket/errors.md +47 -56
- package/content/extensions/components/websocket/index.md +74 -407
- package/content/extensions/components/websocket/usage.md +110 -341
- package/content/extensions/helpers/cron/index.md +51 -160
- package/content/extensions/helpers/crypto/index.md +62 -483
- package/content/extensions/helpers/crypto/reference.md +456 -0
- package/content/extensions/helpers/env/index.md +60 -178
- package/content/extensions/helpers/error/index.md +221 -207
- package/content/extensions/helpers/inversion/index.md +65 -556
- package/content/extensions/helpers/inversion/reference.md +522 -0
- package/content/extensions/helpers/kafka/admin.md +20 -1
- package/content/extensions/helpers/kafka/compile-binary.md +41 -35
- package/content/extensions/helpers/kafka/consumer.md +54 -20
- package/content/extensions/helpers/kafka/examples.md +21 -16
- package/content/extensions/helpers/kafka/index.md +80 -610
- package/content/extensions/helpers/kafka/producer.md +134 -5
- package/content/extensions/helpers/kafka/schema-registry.md +45 -70
- package/content/extensions/helpers/logger/hf-logger.md +193 -0
- package/content/extensions/helpers/logger/index.md +64 -563
- package/content/extensions/helpers/logger/pino.md +85 -0
- package/content/extensions/helpers/logger/reference.md +746 -0
- package/content/extensions/helpers/network/api.md +241 -195
- package/content/extensions/helpers/network/index.md +72 -530
- package/content/extensions/helpers/queue/index.md +72 -900
- package/content/extensions/helpers/queue/reference.md +467 -0
- package/content/extensions/helpers/redis/index.md +73 -645
- package/content/extensions/helpers/redis/reference.md +727 -0
- package/content/extensions/helpers/secrets/index.md +66 -0
- package/content/extensions/helpers/socket-io/api.md +305 -203
- package/content/extensions/helpers/socket-io/index.md +66 -432
- package/content/extensions/helpers/storage/api.md +564 -462
- package/content/extensions/helpers/storage/index.md +77 -573
- package/content/extensions/helpers/types/index.md +66 -499
- package/content/extensions/helpers/types/reference.md +650 -0
- package/content/extensions/helpers/uid/index.md +58 -227
- package/content/extensions/helpers/websocket/api.md +329 -216
- package/content/extensions/helpers/websocket/index.md +65 -503
- package/content/extensions/helpers/worker-thread/index.md +58 -396
- package/content/extensions/helpers/worker-thread/reference.md +428 -0
- package/content/guides/core-concepts/persistent/models.md +1 -1
- package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
- package/content/guides/core-concepts/persistent/transactions.md +1 -1
- package/content/guides/core-concepts/secrets-vault.md +177 -0
- package/content/guides/core-concepts/services.md +1 -1
- package/content/guides/migrations/redis-helpers-migration.md +1 -1
- package/content/guides/migrations/unified-connectors-migration.md +2 -2
- package/content/guides/tutorials/ecommerce-api.md +3 -8
- package/content/references/base/application.md +1 -1
- package/content/references/base/connectors.md +79 -136
- package/content/references/base/datasources-reference.md +599 -0
- package/content/references/base/datasources.md +84 -444
- package/content/references/base/dependency-injection.md +17 -39
- package/content/references/base/filter-system/application-usage.md +69 -121
- package/content/references/base/filter-system/array-operators.md +12 -0
- package/content/references/base/filter-system/comparison-operators.md +12 -0
- package/content/references/base/filter-system/default-filter.md +136 -348
- package/content/references/base/filter-system/fields-order-pagination.md +38 -16
- package/content/references/base/filter-system/index.md +106 -257
- package/content/references/base/filter-system/json-filtering.md +12 -2
- package/content/references/base/filter-system/list-operators.md +16 -2
- package/content/references/base/filter-system/logical-operators.md +13 -0
- package/content/references/base/filter-system/null-operators.md +13 -0
- package/content/references/base/filter-system/pattern-matching.md +12 -0
- package/content/references/base/filter-system/quick-reference.md +11 -2
- package/content/references/base/filter-system/range-operators.md +12 -0
- package/content/references/base/filter-system/tips.md +70 -133
- package/content/references/base/filter-system/use-cases.md +156 -233
- package/content/references/base/middlewares.md +35 -21
- package/content/references/base/models-reference.md +886 -0
- package/content/references/base/models.md +80 -1452
- package/content/references/base/repositories/advanced.md +156 -192
- package/content/references/base/repositories/index.md +77 -650
- package/content/references/base/repositories/mixins.md +22 -18
- package/content/references/base/repositories/relations.md +123 -171
- package/content/references/base/repositories/soft-deletable.md +58 -56
- package/content/references/base/secrets.md +263 -0
- package/content/references/base/services.md +2 -2
- package/content/references/configuration/environment-variables.md +48 -4
- package/content/references/configuration/index.md +49 -31
- package/content/references/quick-reference.md +3 -16
- package/content/references/utilities/crypto.md +35 -76
- package/content/references/utilities/date.md +33 -73
- package/content/references/utilities/index.md +1 -1
- package/content/references/utilities/jsx-reference.md +298 -0
- package/content/references/utilities/jsx.md +82 -525
- package/content/references/utilities/module.md +29 -62
- package/content/references/utilities/parse.md +34 -64
- package/content/references/utilities/performance.md +33 -58
- package/content/references/utilities/promise.md +28 -62
- package/content/references/utilities/request.md +57 -218
- package/content/references/utilities/schema.md +43 -137
- package/content/references/utilities/statuses-reference.md +361 -0
- package/content/references/utilities/statuses.md +63 -667
- package/package.json +8 -8
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Advanced Repository Features
|
|
3
|
-
description: Transactions, hidden properties, and
|
|
3
|
+
description: Transactions, row-level locking, hidden properties, performance, and safety guards
|
|
4
4
|
difficulty: intermediate
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Advanced Repository Features
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Exhaustive reference for everything beyond basic CRUD - transactions, row-level locking, hidden-property exclusion, performance tuning, return-type inference, debugging, and the built-in safety guards. For the common tasks, start with the [Repositories overview](/references/base/repositories/).
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
**Files:**
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
-
|
|
18
|
-
-
|
|
13
|
+
- [`packages/core/src/base/repositories/core/abstract.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/core/abstract.ts) - engine-neutral `AbstractRepository`
|
|
14
|
+
- [`packages/core/src/base/repositories/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/common/types.ts) - `IExtraOptions`, `TLockOptions`, `TCount`, `TDataRange`
|
|
15
|
+
- [`packages/core/src/connectors/postgres/repositories/core/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/core/base.ts) - `RelationalBaseRepository` - hidden-column exclusion, `buildQuery`, `resolveConnector`, lock validation
|
|
16
|
+
- [`packages/core/src/connectors/postgres/repositories/core/readable.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/core/readable.ts) - `ReadableRelationalRepository` - Core API vs. Query API selection, `shouldQueryRange`
|
|
17
|
+
- [`packages/core/src/connectors/postgres/repositories/core/persistable.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/core/persistable.ts) - `PersistableRelationalRepository` - create/update/delete, empty-where guard
|
|
18
|
+
- [`packages/core/src/connectors/postgres/repositories/core/soft-deletable.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/core/soft-deletable.ts) - `SoftDeletableRelationalRepository`
|
|
19
|
+
- [`packages/core/src/connectors/postgres/repositories/dialect/update.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/update.ts) - `UpdateBuilder` - nested JSON path updates
|
|
20
|
+
- [`packages/core/src/connectors/postgres/datasources/abstract.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/datasources/abstract.ts) - `beginTransaction()`, isolation levels
|
|
19
21
|
|
|
20
22
|
## Transactions
|
|
21
23
|
|
|
22
|
-
Orchestrate atomic operations across multiple repositories.
|
|
24
|
+
Orchestrate atomic operations across multiple repositories. `repository.beginTransaction()` delegates to `dataSource.beginTransaction()` and returns an `IDatabaseTransaction` (`isActive`, `commit()`, `rollback()`, `connector`, `isolationLevel`).
|
|
23
25
|
|
|
24
|
-
### Basic
|
|
26
|
+
### Basic transaction
|
|
25
27
|
|
|
26
28
|
```typescript
|
|
27
29
|
const tx = await repository.beginTransaction();
|
|
@@ -30,66 +32,67 @@ try {
|
|
|
30
32
|
// All operations use the same transaction
|
|
31
33
|
const user = await userRepository.create({
|
|
32
34
|
data: { name: 'Alice', email: 'alice@example.com' },
|
|
33
|
-
options: { transaction: tx }
|
|
35
|
+
options: { transaction: tx },
|
|
34
36
|
});
|
|
35
37
|
|
|
36
38
|
const profile = await profileRepository.create({
|
|
37
39
|
data: { userId: user.data.id, bio: 'Hello!' },
|
|
38
|
-
options: { transaction: tx }
|
|
40
|
+
options: { transaction: tx },
|
|
39
41
|
});
|
|
40
42
|
|
|
41
|
-
// Commit if all succeeded
|
|
42
43
|
await tx.commit();
|
|
43
|
-
|
|
44
44
|
return { user: user.data, profile: profile.data };
|
|
45
45
|
} catch (error) {
|
|
46
|
-
// Rollback on any error
|
|
47
46
|
await tx.rollback();
|
|
48
47
|
throw error;
|
|
49
48
|
}
|
|
50
49
|
```
|
|
51
50
|
|
|
52
|
-
|
|
51
|
+
> [!WARNING] `rollback()` can throw
|
|
52
|
+
> A failed `COMMIT` or `ROLLBACK` **throws** rather than resolving as success - a poisoned connection is destroyed rather than returned to the pool. Because `rollback()` can throw and is normally called from a `catch`, nest it in its own `try...catch` if the rollback error matters; a `rollback()` called after the transaction already failed is a silent no-op, so the `catch { await tx.rollback(); throw error; }` pattern shown above is always safe. See [DataSources - Full Reference](/references/base/datasources-reference#transaction-support) for the full commit/rollback lifecycle.
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
### Isolation levels
|
|
55
|
+
|
|
56
|
+
Control how transactions interact with concurrent operations using the `IsolationLevels` const class:
|
|
55
57
|
|
|
56
58
|
```typescript
|
|
59
|
+
import { IsolationLevels } from '@venizia/ignis/postgres';
|
|
60
|
+
|
|
57
61
|
const tx = await repository.beginTransaction({
|
|
58
|
-
isolationLevel:
|
|
62
|
+
isolationLevel: IsolationLevels.SERIALIZABLE,
|
|
59
63
|
});
|
|
60
64
|
```
|
|
61
65
|
|
|
62
|
-
| Level | Description | Use
|
|
66
|
+
| Level | Description | Use case |
|
|
63
67
|
|-------|-------------|----------|
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
| `SERIALIZABLE` | Full isolation, prevents anomalies | Financial, inventory |
|
|
68
|
+
| `IsolationLevels.READ_COMMITTED` | Default. See committed data only | Most applications |
|
|
69
|
+
| `IsolationLevels.REPEATABLE_READ` | Consistent reads within transaction | Reports, analytics |
|
|
70
|
+
| `IsolationLevels.SERIALIZABLE` | Full isolation, prevents anomalies | Financial, inventory |
|
|
67
71
|
|
|
68
|
-
|
|
72
|
+
A plain string literal (`'SERIALIZABLE'`) works too - `isolationLevel` is typed `TIsolationLevel`, a string union the const class's values satisfy.
|
|
73
|
+
|
|
74
|
+
### Transaction with multiple repositories
|
|
69
75
|
|
|
70
76
|
```typescript
|
|
71
77
|
async function transferFunds(fromId: string, toId: string, amount: number) {
|
|
72
78
|
const tx = await accountRepository.beginTransaction();
|
|
73
79
|
|
|
74
80
|
try {
|
|
75
|
-
// Debit source account
|
|
76
81
|
await accountRepository.updateById({
|
|
77
82
|
id: fromId,
|
|
78
83
|
data: { balance: sql`balance - ${amount}` },
|
|
79
|
-
options: { transaction: tx }
|
|
84
|
+
options: { transaction: tx },
|
|
80
85
|
});
|
|
81
86
|
|
|
82
|
-
// Credit destination account
|
|
83
87
|
await accountRepository.updateById({
|
|
84
88
|
id: toId,
|
|
85
89
|
data: { balance: sql`balance + ${amount}` },
|
|
86
|
-
options: { transaction: tx }
|
|
90
|
+
options: { transaction: tx },
|
|
87
91
|
});
|
|
88
92
|
|
|
89
|
-
// Record the transfer
|
|
90
93
|
await transferRepository.create({
|
|
91
94
|
data: { fromId, toId, amount, status: 'completed' },
|
|
92
|
-
options: { transaction: tx }
|
|
95
|
+
options: { transaction: tx },
|
|
93
96
|
});
|
|
94
97
|
|
|
95
98
|
await tx.commit();
|
|
@@ -100,14 +103,13 @@ async function transferFunds(fromId: string, toId: string, amount: number) {
|
|
|
100
103
|
}
|
|
101
104
|
```
|
|
102
105
|
|
|
103
|
-
|
|
104
106
|
## Row-Level Locking
|
|
105
107
|
|
|
106
108
|
Acquire pessimistic locks on selected rows within a transaction using PostgreSQL's `SELECT ... FOR UPDATE/SHARE` syntax.
|
|
107
109
|
|
|
108
|
-
### Basic
|
|
110
|
+
### Basic usage
|
|
109
111
|
|
|
110
|
-
Pass `lock` in options alongside a `transaction`:
|
|
112
|
+
Pass `lock` in `options` alongside a `transaction`:
|
|
111
113
|
|
|
112
114
|
```typescript
|
|
113
115
|
const tx = await repository.beginTransaction();
|
|
@@ -136,9 +138,9 @@ try {
|
|
|
136
138
|
}
|
|
137
139
|
```
|
|
138
140
|
|
|
139
|
-
### Lock
|
|
141
|
+
### Lock strengths
|
|
140
142
|
|
|
141
|
-
Use the `LockStrengths`
|
|
143
|
+
Use the `LockStrengths` const class or string literals:
|
|
142
144
|
|
|
143
145
|
```typescript
|
|
144
146
|
import { LockStrengths } from '@venizia/ignis';
|
|
@@ -150,16 +152,16 @@ lock: { strength: LockStrengths.UPDATE }
|
|
|
150
152
|
lock: { strength: 'update' }
|
|
151
153
|
```
|
|
152
154
|
|
|
153
|
-
| Strength | SQL | Use
|
|
155
|
+
| Strength | SQL | Use case |
|
|
154
156
|
|----------|-----|----------|
|
|
155
157
|
| `update` | `FOR UPDATE` | Exclusive lock for writes |
|
|
156
158
|
| `no key update` | `FOR NO KEY UPDATE` | Exclusive lock, allows concurrent `FOR KEY SHARE` |
|
|
157
159
|
| `share` | `FOR SHARE` | Shared read lock, prevents writes |
|
|
158
160
|
| `key share` | `FOR KEY SHARE` | Weakest lock, only prevents key changes |
|
|
159
161
|
|
|
160
|
-
### Wait
|
|
162
|
+
### Wait behavior
|
|
161
163
|
|
|
162
|
-
Control what happens when rows are already locked
|
|
164
|
+
Control what happens when rows are already locked via `lock.config`:
|
|
163
165
|
|
|
164
166
|
```typescript
|
|
165
167
|
// Skip locked rows (queue-style worker pattern)
|
|
@@ -187,10 +189,12 @@ const item = await repository.findOne({
|
|
|
187
189
|
| `{ noWait: true }` | `FOR UPDATE NOWAIT` | Throw error immediately if locked |
|
|
188
190
|
| `{ skipLocked: true }` | `FOR UPDATE SKIP LOCKED` | Silently skip locked rows |
|
|
189
191
|
|
|
192
|
+
`noWait` and `skipLocked` are mutually exclusive at the type level (`TLockConfig`).
|
|
193
|
+
|
|
190
194
|
### Constraints
|
|
191
195
|
|
|
192
196
|
> [!WARNING]
|
|
193
|
-
> Row-level locking requires a **transaction** and is **incompatible with `include`/`fields`** in the filter (these
|
|
197
|
+
> Row-level locking requires a **transaction** and is **incompatible with `include`/`fields`** in the filter (these force the Drizzle Query API, which does not support `.for()`).
|
|
194
198
|
|
|
195
199
|
```typescript
|
|
196
200
|
// Error - no transaction
|
|
@@ -199,15 +203,14 @@ await repository.findOne({
|
|
|
199
203
|
options: { lock: { strength: 'update' } },
|
|
200
204
|
});
|
|
201
205
|
|
|
202
|
-
// Error - include
|
|
206
|
+
// Error - include forces the Query API
|
|
203
207
|
await repository.findOne({
|
|
204
208
|
filter: { where: { id: '123' }, include: [{ relation: 'posts' }] },
|
|
205
209
|
options: { transaction: tx, lock: { strength: 'update' } },
|
|
206
210
|
});
|
|
207
211
|
```
|
|
208
212
|
|
|
209
|
-
**Supported methods:** `find`, `findOne`, `findById`
|
|
210
|
-
|
|
213
|
+
**Supported methods:** `find`, `findOne`, `findById` (delegates to `findOne`). Not `count`/`existsWith`.
|
|
211
214
|
|
|
212
215
|
## Hidden Properties
|
|
213
216
|
|
|
@@ -229,7 +232,7 @@ export class User extends BaseEntity<typeof User.schema> {
|
|
|
229
232
|
}
|
|
230
233
|
```
|
|
231
234
|
|
|
232
|
-
### Automatic
|
|
235
|
+
### Automatic exclusion
|
|
233
236
|
|
|
234
237
|
Hidden properties are excluded at the **SQL level** for maximum security:
|
|
235
238
|
|
|
@@ -241,43 +244,41 @@ const user = await userRepository.findById({ id: '123' });
|
|
|
241
244
|
|
|
242
245
|
// Write operations exclude hidden from RETURNING clause
|
|
243
246
|
const created = await userRepository.create({
|
|
244
|
-
data: { email: 'new@example.com', password: 'hashed_secret' }
|
|
247
|
+
data: { email: 'new@example.com', password: 'hashed_secret' },
|
|
245
248
|
});
|
|
246
249
|
// Result: { count: 1, data: { id: '456', email: 'new@example.com' } }
|
|
247
250
|
// Note: password stored in DB but not returned
|
|
248
251
|
```
|
|
249
252
|
|
|
250
|
-
### Filtering by
|
|
253
|
+
### Filtering by hidden properties
|
|
251
254
|
|
|
252
255
|
You **can** filter by hidden properties - you just can't see them in results:
|
|
253
256
|
|
|
254
257
|
```typescript
|
|
255
258
|
// This works! Finds user but password not in result
|
|
256
259
|
const user = await userRepository.findOne({
|
|
257
|
-
filter: { where: { password: 'hashed_value' } }
|
|
260
|
+
filter: { where: { password: 'hashed_value' } },
|
|
258
261
|
});
|
|
259
262
|
```
|
|
260
263
|
|
|
261
|
-
### Relations with
|
|
264
|
+
### Relations with hidden properties
|
|
262
265
|
|
|
263
|
-
Hidden properties are also excluded from included relations
|
|
266
|
+
Hidden properties are also excluded from included relations - see [Relations & Includes](./relations#hidden-properties-in-relations).
|
|
264
267
|
|
|
265
268
|
```typescript
|
|
266
269
|
const post = await postRepository.findOne({
|
|
267
|
-
filter: {
|
|
268
|
-
include: [{ relation: 'author' }]
|
|
269
|
-
}
|
|
270
|
+
filter: { include: [{ relation: 'author' }] },
|
|
270
271
|
});
|
|
271
272
|
// post.author will NOT include password, secret, etc.
|
|
272
273
|
```
|
|
273
274
|
|
|
274
|
-
### Accessing
|
|
275
|
+
### Accessing hidden data
|
|
275
276
|
|
|
276
|
-
When you need hidden fields (e.g., for authentication), bypass the repository:
|
|
277
|
+
When you need hidden fields (e.g., for authentication), bypass the repository via the `connector` getter:
|
|
277
278
|
|
|
278
279
|
```typescript
|
|
279
280
|
// Direct connector access - includes all fields
|
|
280
|
-
const connector = userRepository.
|
|
281
|
+
const connector = userRepository.connector;
|
|
281
282
|
const [fullUser] = await connector
|
|
282
283
|
.select()
|
|
283
284
|
.from(User.schema)
|
|
@@ -285,12 +286,11 @@ const [fullUser] = await connector
|
|
|
285
286
|
// fullUser includes password, secret, apiKey
|
|
286
287
|
```
|
|
287
288
|
|
|
288
|
-
|
|
289
289
|
## Performance Optimization
|
|
290
290
|
|
|
291
|
-
### Core API for
|
|
291
|
+
### Core API for flat queries
|
|
292
292
|
|
|
293
|
-
The repository automatically uses Drizzle's Core API (faster) for simple queries:
|
|
293
|
+
The repository automatically uses Drizzle's Core API (faster) for simple queries; a query with `include` or `fields` forces the Query API instead:
|
|
294
294
|
|
|
295
295
|
```typescript
|
|
296
296
|
// Automatically optimized - uses Core API
|
|
@@ -298,8 +298,8 @@ const users = await repository.find({
|
|
|
298
298
|
filter: {
|
|
299
299
|
where: { status: 'active' },
|
|
300
300
|
limit: 10,
|
|
301
|
-
order: ['createdAt DESC']
|
|
302
|
-
}
|
|
301
|
+
order: ['createdAt DESC'],
|
|
302
|
+
},
|
|
303
303
|
});
|
|
304
304
|
// Uses: db.select().from(table).where(...).orderBy(...).limit(10)
|
|
305
305
|
|
|
@@ -307,41 +307,38 @@ const users = await repository.find({
|
|
|
307
307
|
const usersWithPosts = await repository.find({
|
|
308
308
|
filter: {
|
|
309
309
|
where: { status: 'active' },
|
|
310
|
-
include: [{ relation: 'posts' }]
|
|
311
|
-
}
|
|
310
|
+
include: [{ relation: 'posts' }],
|
|
311
|
+
},
|
|
312
312
|
});
|
|
313
313
|
// Uses: db.query.tableName.findMany({ with: { posts: true }, ... })
|
|
314
314
|
```
|
|
315
315
|
|
|
316
|
-
| Filter
|
|
316
|
+
| Filter options | API used | Performance |
|
|
317
317
|
|----------------|----------|-------------|
|
|
318
318
|
| `where`, `limit`, `order`, `offset`/`skip` only | Core API | ~15-20% faster |
|
|
319
319
|
| Has `include` (relations) | Query API | Standard |
|
|
320
320
|
| Has `fields` selection | Query API | Standard |
|
|
321
321
|
|
|
322
|
-
### Always
|
|
322
|
+
### Always use limit
|
|
323
323
|
|
|
324
324
|
Prevent memory exhaustion on large tables:
|
|
325
325
|
|
|
326
326
|
```typescript
|
|
327
327
|
// Good - bounded result set
|
|
328
328
|
await repository.find({
|
|
329
|
-
filter: {
|
|
330
|
-
where: { status: 'active' },
|
|
331
|
-
limit: 100
|
|
332
|
-
}
|
|
329
|
+
filter: { where: { status: 'active' }, limit: 100 },
|
|
333
330
|
});
|
|
334
331
|
|
|
335
332
|
// Dangerous - could return millions of rows
|
|
336
333
|
await repository.find({
|
|
337
|
-
filter: { where: { status: 'active' } }
|
|
334
|
+
filter: { where: { status: 'active' } },
|
|
338
335
|
});
|
|
339
336
|
```
|
|
340
337
|
|
|
341
338
|
> [!NOTE]
|
|
342
|
-
> `find()` always applies a default limit
|
|
339
|
+
> `find()` always applies a default limit when no `limit` is set in the filter - the model's `@model({ settings: { defaultLimit } })` if declared, otherwise the global default of `10`. Pass an explicit `limit` in the filter to override either default. `findOne`/`findById` are unaffected - they force `limit: 1` on the Core API path regardless.
|
|
343
340
|
|
|
344
|
-
### Pagination with
|
|
341
|
+
### Pagination with data range
|
|
345
342
|
|
|
346
343
|
Use `shouldQueryRange` to get both data and total count in a single call:
|
|
347
344
|
|
|
@@ -351,9 +348,9 @@ const result = await userRepository.find({
|
|
|
351
348
|
where: { status: 'active' },
|
|
352
349
|
limit: 20,
|
|
353
350
|
skip: 40,
|
|
354
|
-
order: ['createdAt DESC']
|
|
351
|
+
order: ['createdAt DESC'],
|
|
355
352
|
},
|
|
356
|
-
options: { shouldQueryRange: true }
|
|
353
|
+
options: { shouldQueryRange: true },
|
|
357
354
|
});
|
|
358
355
|
|
|
359
356
|
// Result type: { data: User[], range: { start: number, end: number, total: number } }
|
|
@@ -361,22 +358,18 @@ const result = await userRepository.find({
|
|
|
361
358
|
// Example: { data: [...20 users], range: { start: 40, end: 59, total: 150 } }
|
|
362
359
|
```
|
|
363
360
|
|
|
364
|
-
|
|
361
|
+
- **Parallel by default.** `find` and `count` execute concurrently via `Promise.all`.
|
|
362
|
+
- **Sequential inside a transaction.** A transaction connector wraps a single client, so parallel queries on it are not safe - the two queries run one after another instead.
|
|
365
363
|
|
|
366
|
-
### WeakMap
|
|
367
|
-
|
|
368
|
-
The filter builder caches table column metadata, avoiding repeated reflection:
|
|
369
|
-
|
|
370
|
-
```typescript
|
|
371
|
-
// Internal optimization - automatic
|
|
372
|
-
// First query: getTableColumns(schema) -> cached in WeakMap
|
|
373
|
-
// Subsequent queries: retrieved from WeakMap cache
|
|
374
|
-
```
|
|
364
|
+
### WeakMap cache
|
|
375
365
|
|
|
366
|
+
- **Column metadata is cached per schema.** The filter builder caches table column metadata (`getCachedColumns`) to avoid repeated reflection.
|
|
367
|
+
- **Populated on first access.** The first access calls Drizzle's `getTableColumns()` and caches the result; subsequent queries retrieve it from the `WeakMap`.
|
|
368
|
+
- **No configuration needed.** The cache is automatic.
|
|
376
369
|
|
|
377
370
|
## TypeScript Return Types
|
|
378
371
|
|
|
379
|
-
### shouldReturn
|
|
372
|
+
### shouldReturn inference
|
|
380
373
|
|
|
381
374
|
Repository methods infer return types based on `shouldReturn`:
|
|
382
375
|
|
|
@@ -384,14 +377,14 @@ Repository methods infer return types based on `shouldReturn`:
|
|
|
384
377
|
// shouldReturn: false - TypeScript knows data is null
|
|
385
378
|
const result1 = await repository.create({
|
|
386
379
|
data: { name: 'John' },
|
|
387
|
-
options: { shouldReturn: false }
|
|
380
|
+
options: { shouldReturn: false },
|
|
388
381
|
});
|
|
389
382
|
// Type: Promise<{ count: number; data: undefined | null }>
|
|
390
383
|
|
|
391
384
|
// shouldReturn: true (default) - TypeScript knows data is the entity
|
|
392
385
|
const result2 = await repository.create({
|
|
393
386
|
data: { name: 'John' },
|
|
394
|
-
options: { shouldReturn: true }
|
|
387
|
+
options: { shouldReturn: true },
|
|
395
388
|
});
|
|
396
389
|
// Type: Promise<{ count: number; data: User }>
|
|
397
390
|
console.log(result2.data.name); // 'John' - fully typed!
|
|
@@ -399,55 +392,48 @@ console.log(result2.data.name); // 'John' - fully typed!
|
|
|
399
392
|
// Array operations
|
|
400
393
|
const results = await repository.createAll({
|
|
401
394
|
data: [{ name: 'John' }, { name: 'Jane' }],
|
|
402
|
-
options: { shouldReturn: true }
|
|
395
|
+
options: { shouldReturn: true },
|
|
403
396
|
});
|
|
404
397
|
// Type: Promise<{ count: number; data: User[] }>
|
|
405
398
|
```
|
|
406
399
|
|
|
407
|
-
### Generic
|
|
400
|
+
### Generic return types
|
|
408
401
|
|
|
409
402
|
Override return types for queries with relations:
|
|
410
403
|
|
|
411
404
|
```typescript
|
|
412
|
-
// Define expected return type
|
|
413
405
|
type UserWithPosts = User & {
|
|
414
406
|
posts: Post[];
|
|
415
407
|
};
|
|
416
408
|
|
|
417
|
-
// Use generic override
|
|
418
409
|
const user = await userRepository.findOne<UserWithPosts>({
|
|
419
410
|
filter: {
|
|
420
411
|
where: { id: '123' },
|
|
421
|
-
include: [{ relation: 'posts' }]
|
|
422
|
-
}
|
|
412
|
+
include: [{ relation: 'posts' }],
|
|
413
|
+
},
|
|
423
414
|
});
|
|
424
415
|
|
|
425
|
-
// TypeScript knows the structure!
|
|
426
416
|
if (user) {
|
|
427
417
|
console.log(user.posts[0].title); // Fully typed
|
|
428
418
|
}
|
|
429
419
|
```
|
|
430
420
|
|
|
431
|
-
**Supported
|
|
421
|
+
**Supported methods:**
|
|
432
422
|
- `find<R>()`, `findOne<R>()`, `findById<R>()`
|
|
433
423
|
- `create<R>()`, `createAll<R>()`
|
|
434
424
|
- `updateById<R>()`, `updateAll<R>()`, `updateBy<R>()`
|
|
435
425
|
- `deleteById<R>()`, `deleteAll<R>()`, `deleteBy<R>()`
|
|
436
426
|
|
|
437
|
-
|
|
438
427
|
## Debugging
|
|
439
428
|
|
|
440
|
-
### Log
|
|
429
|
+
### Log option
|
|
441
430
|
|
|
442
|
-
Enable logging for specific operations:
|
|
431
|
+
Enable logging for specific operations via `TRepositoryLogOptions` (`{ use: boolean; level?: TLogLevel }`):
|
|
443
432
|
|
|
444
433
|
```typescript
|
|
445
|
-
// Enable debug logging
|
|
446
434
|
await repository.create({
|
|
447
435
|
data: { name: 'John', email: 'john@example.com' },
|
|
448
|
-
options: {
|
|
449
|
-
log: { use: true, level: 'debug' }
|
|
450
|
-
}
|
|
436
|
+
options: { log: { use: true, level: 'debug' } },
|
|
451
437
|
});
|
|
452
438
|
// Output: [_create] Executing with opts: { data: [...], options: {...} }
|
|
453
439
|
|
|
@@ -455,15 +441,15 @@ await repository.create({
|
|
|
455
441
|
await repository.updateById({
|
|
456
442
|
id: '123',
|
|
457
443
|
data: { name: 'Jane' },
|
|
458
|
-
options: { log: { use: true, level: 'info' } }
|
|
444
|
+
options: { log: { use: true, level: 'info' } },
|
|
459
445
|
});
|
|
460
446
|
```
|
|
461
447
|
|
|
462
|
-
**Available on:** `create`, `createAll`, `updateById`, `updateAll`, `updateBy`, `deleteById`, `deleteAll`, `deleteBy`
|
|
448
|
+
**Available on:** `create`, `createAll`, `updateById`, `updateAll`, `updateBy`, `deleteById`, `deleteAll`, `deleteBy` - every write operation that goes through the internal `_create`, `_update`, or `_delete` methods.
|
|
463
449
|
|
|
464
|
-
### Query
|
|
450
|
+
### Query interface validation
|
|
465
451
|
|
|
466
|
-
The repository validates schema registration on
|
|
452
|
+
The repository validates schema registration on first Query API access:
|
|
467
453
|
|
|
468
454
|
```typescript
|
|
469
455
|
// If schema key doesn't match, you get a helpful error:
|
|
@@ -473,12 +459,11 @@ The repository validates schema registration on startup:
|
|
|
473
459
|
// | Ensure the model's TABLE_NAME matches the schema registration key
|
|
474
460
|
```
|
|
475
461
|
|
|
476
|
-
|
|
477
462
|
## Safety Features
|
|
478
463
|
|
|
479
|
-
### Empty
|
|
464
|
+
### Empty where protection
|
|
480
465
|
|
|
481
|
-
|
|
466
|
+
`updateAll`/`updateBy`/`deleteAll`/`deleteBy` refuse an empty `where` unless `force: true` is passed - prevents accidental mass updates/deletes:
|
|
482
467
|
|
|
483
468
|
```typescript
|
|
484
469
|
// Throws error - empty where without force
|
|
@@ -487,7 +472,7 @@ await repository.deleteAll({ where: {} });
|
|
|
487
472
|
// Explicit force flag - logs warning, proceeds
|
|
488
473
|
await repository.deleteAll({
|
|
489
474
|
where: {},
|
|
490
|
-
options: { force: true }
|
|
475
|
+
options: { force: true },
|
|
491
476
|
});
|
|
492
477
|
// Warning: [_delete] Entity: User | Performing delete with empty condition
|
|
493
478
|
```
|
|
@@ -498,25 +483,23 @@ await repository.deleteAll({
|
|
|
498
483
|
| Valid `where` | Executes normally | Executes normally |
|
|
499
484
|
|
|
500
485
|
> [!NOTE]
|
|
501
|
-
>
|
|
486
|
+
> `updateById` and `deleteById` always have a non-empty where (`{ id }`) so they are not affected by this guard.
|
|
502
487
|
|
|
503
|
-
### Transaction
|
|
488
|
+
### Transaction safety
|
|
504
489
|
|
|
505
|
-
|
|
490
|
+
`resolveConnector` validates transaction state before use:
|
|
506
491
|
|
|
507
492
|
```typescript
|
|
508
493
|
// If a transaction has already been committed or rolled back:
|
|
509
494
|
// Error: [UserRepository][resolveConnector] Transaction is no longer active
|
|
510
495
|
```
|
|
511
496
|
|
|
512
|
-
|
|
513
497
|
## Direct Connector Access
|
|
514
498
|
|
|
515
|
-
For advanced queries not supported by the repository API:
|
|
499
|
+
For advanced queries not supported by the repository API, `repository.connector` is a getter (not a method) that resolves the datasource's Drizzle connector:
|
|
516
500
|
|
|
517
501
|
```typescript
|
|
518
|
-
|
|
519
|
-
const connector = repository.getConnector();
|
|
502
|
+
const connector = repository.connector;
|
|
520
503
|
|
|
521
504
|
// Raw Drizzle query
|
|
522
505
|
const results = await connector
|
|
@@ -532,19 +515,22 @@ const results = await connector
|
|
|
532
515
|
// Use with caution - bypasses repository features like hidden properties
|
|
533
516
|
```
|
|
534
517
|
|
|
535
|
-
|
|
536
518
|
## Repository Class Hierarchy
|
|
537
519
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
|
541
|
-
|
|
542
|
-
| `
|
|
543
|
-
| `
|
|
544
|
-
| `
|
|
545
|
-
| `
|
|
520
|
+
The PostgreSQL connector's canonical names carry the engine in the class name; the historical `*Repository` names remain available as compatibility aliases re-exporting the exact same classes.
|
|
521
|
+
|
|
522
|
+
| Canonical class | Alias | Scope | Description |
|
|
523
|
+
|---|---|---|---|
|
|
524
|
+
| `AbstractRepository` | - | N/A | Engine-neutral abstract base (`src/base`), defines all method signatures, lazy `dataSource`/`entity` resolution. Plain `BaseHelper` subclass, no mixin composition. |
|
|
525
|
+
| `RelationalBaseRepository` | `PostgresBaseRepository` | N/A | PostgreSQL connector base. Adds `FilterBuilder`/`UpdateBuilder`, hidden-column exclusion (`getHiddenProperties`/`getVisibleProperties`), default-filter application (`getDefaultFilter`/`applyDefaultFilter`) - the behavior formerly provided by the now-removed `FieldsVisibilityMixin`/`DefaultFilterMixin` (see [Repository Mixins](./mixins)). |
|
|
526
|
+
| `ReadableRelationalRepository` | `ReadableRepository` | `READ_ONLY` | Read-only operations (`find`, `findOne`, `findById`, `count`, `existsWith`). Write operations throw errors. |
|
|
527
|
+
| `PersistableRelationalRepository` | `PersistableRepository` | `READ_WRITE` | Adds write operations (`create`, `update`, `delete`) with `UpdateBuilder`. |
|
|
528
|
+
| `DefaultRelationalRepository` | `DefaultCRUDRepository` | `READ_WRITE` | Extends `PersistableRelationalRepository` with no additional logic - **recommended default**. |
|
|
529
|
+
| `SoftDeletableRelationalRepository` | `SoftDeletableRepository` | `READ_WRITE` | Extends `DefaultRelationalRepository` with soft delete + restore operations - see [SoftDeletableRepository](./soft-deletable). |
|
|
546
530
|
|
|
547
|
-
|
|
531
|
+
Code samples throughout the docs use the alias names (`DefaultCRUDRepository`, `ReadableRepository`) since that is what `@venizia/ignis/postgres` code most commonly imports today.
|
|
532
|
+
|
|
533
|
+
### Creating a read-only repository
|
|
548
534
|
|
|
549
535
|
```typescript
|
|
550
536
|
@repository({ model: AuditLog, dataSource: PostgresDataSource })
|
|
@@ -554,7 +540,7 @@ export class AuditLogRepository extends ReadableRepository<typeof AuditLog.schem
|
|
|
554
540
|
}
|
|
555
541
|
```
|
|
556
542
|
|
|
557
|
-
### Alias
|
|
543
|
+
### Alias methods
|
|
558
544
|
|
|
559
545
|
`AbstractRepository` provides two alias methods for convenience:
|
|
560
546
|
|
|
@@ -563,7 +549,6 @@ export class AuditLogRepository extends ReadableRepository<typeof AuditLog.schem
|
|
|
563
549
|
|
|
564
550
|
Both accept the same parameters (`where`, `data`/`options`) and support `shouldReturn` and `force` options.
|
|
565
551
|
|
|
566
|
-
|
|
567
552
|
## Default Filter Bypass
|
|
568
553
|
|
|
569
554
|
When models have a `defaultFilter` configured, you can bypass it for admin/maintenance operations:
|
|
@@ -571,14 +556,14 @@ When models have a `defaultFilter` configured, you can bypass it for admin/maint
|
|
|
571
556
|
```typescript
|
|
572
557
|
// Normal query - default filter applies
|
|
573
558
|
await repository.find({
|
|
574
|
-
filter: { where: { status: 'active' } }
|
|
559
|
+
filter: { where: { status: 'active' } },
|
|
575
560
|
});
|
|
576
561
|
// WHERE isDeleted = false AND status = 'active' (if model has soft-delete default)
|
|
577
562
|
|
|
578
563
|
// Admin query - bypass default filter
|
|
579
564
|
await repository.find({
|
|
580
565
|
filter: { where: { status: 'active' } },
|
|
581
|
-
options: { shouldSkipDefaultFilter: true }
|
|
566
|
+
options: { shouldSkipDefaultFilter: true },
|
|
582
567
|
});
|
|
583
568
|
// WHERE status = 'active' (includes deleted records)
|
|
584
569
|
```
|
|
@@ -605,21 +590,20 @@ await repository.updateAll({
|
|
|
605
590
|
data: { isDeleted: true },
|
|
606
591
|
options: {
|
|
607
592
|
transaction: tx,
|
|
608
|
-
shouldSkipDefaultFilter: true
|
|
609
|
-
}
|
|
593
|
+
shouldSkipDefaultFilter: true,
|
|
594
|
+
},
|
|
610
595
|
});
|
|
611
596
|
await tx.commit();
|
|
612
597
|
```
|
|
613
598
|
|
|
614
599
|
> [!TIP]
|
|
615
|
-
> See [Default Filter](../filter-system/default-filter
|
|
616
|
-
|
|
600
|
+
> See [Default Filter](../filter-system/default-filter) for full documentation on configuring model default filters.
|
|
617
601
|
|
|
618
602
|
## Nested JSON Updates
|
|
619
603
|
|
|
620
|
-
Repositories support updating specific fields within `json
|
|
604
|
+
Repositories support updating specific fields within `json`/`jsonb` columns without overwriting the entire object, via **JSON path notation** in the update data - handled by `UpdateBuilder`.
|
|
621
605
|
|
|
622
|
-
### Basic
|
|
606
|
+
### Basic usage
|
|
623
607
|
|
|
624
608
|
Use dot notation keys to target nested properties:
|
|
625
609
|
|
|
@@ -631,51 +615,49 @@ await repository.updateById({
|
|
|
631
615
|
id: '123',
|
|
632
616
|
data: {
|
|
633
617
|
// Update only the theme, preserving other fields
|
|
634
|
-
'metadata.theme': 'dark'
|
|
635
|
-
}
|
|
618
|
+
'metadata.theme': 'dark',
|
|
619
|
+
},
|
|
636
620
|
});
|
|
637
621
|
|
|
638
622
|
// New value: { theme: 'dark', notifications: { email: true } }
|
|
639
623
|
```
|
|
640
624
|
|
|
641
|
-
### Supported
|
|
625
|
+
### Supported features
|
|
642
626
|
|
|
643
|
-
- **Deep
|
|
644
|
-
- **Array
|
|
645
|
-
- **Auto-
|
|
646
|
-
- **Type
|
|
647
|
-
- **Multiple
|
|
648
|
-
- **Mixed
|
|
627
|
+
- **Deep nesting:** update properties at any depth (e.g., `settings.display.font.size`).
|
|
628
|
+
- **Array access:** update array elements by index (e.g., `tags[0]`).
|
|
629
|
+
- **Auto-creation:** creates missing intermediate keys automatically (`jsonb_set` with `create_missing = true`).
|
|
630
|
+
- **Type safety:** validates that the target column is a JSON/JSONB type.
|
|
631
|
+
- **Multiple updates:** multiple updates to the same column are chained as nested `jsonb_set` calls.
|
|
632
|
+
- **Mixed updates:** combine regular column updates with JSON path updates in a single call.
|
|
649
633
|
|
|
650
634
|
### Examples
|
|
651
635
|
|
|
652
|
-
#### Deeply
|
|
636
|
+
#### Deeply nested updates
|
|
653
637
|
|
|
654
638
|
```typescript
|
|
655
639
|
await repository.updateById({
|
|
656
640
|
id: '123',
|
|
657
641
|
data: {
|
|
658
642
|
'metadata.settings.display.fontSize': 16,
|
|
659
|
-
'metadata.settings.display.showSidebar': true
|
|
660
|
-
}
|
|
643
|
+
'metadata.settings.display.showSidebar': true,
|
|
644
|
+
},
|
|
661
645
|
});
|
|
662
646
|
```
|
|
663
647
|
|
|
664
|
-
#### Array
|
|
648
|
+
#### Array element updates
|
|
665
649
|
|
|
666
650
|
```typescript
|
|
667
651
|
await repository.updateById({
|
|
668
652
|
id: '123',
|
|
669
653
|
data: {
|
|
670
654
|
// Set the first address as primary
|
|
671
|
-
'metadata.addresses[0].primary': true
|
|
672
|
-
}
|
|
655
|
+
'metadata.addresses[0].primary': true,
|
|
656
|
+
},
|
|
673
657
|
});
|
|
674
658
|
```
|
|
675
659
|
|
|
676
|
-
#### Mixed
|
|
677
|
-
|
|
678
|
-
You can mix regular column updates with JSON path updates:
|
|
660
|
+
#### Mixed updates (regular + JSON)
|
|
679
661
|
|
|
680
662
|
```typescript
|
|
681
663
|
await repository.updateById({
|
|
@@ -683,29 +665,29 @@ await repository.updateById({
|
|
|
683
665
|
data: {
|
|
684
666
|
status: 'active', // Regular column
|
|
685
667
|
'metadata.lastLogin': now, // JSON path
|
|
686
|
-
'preferences.lang': 'en' // Another JSON path
|
|
687
|
-
}
|
|
668
|
+
'preferences.lang': 'en', // Another JSON path
|
|
669
|
+
},
|
|
688
670
|
});
|
|
689
671
|
```
|
|
690
672
|
|
|
691
|
-
### Security &
|
|
673
|
+
### Security & validation
|
|
692
674
|
|
|
693
675
|
The framework validates JSON paths to prevent SQL injection:
|
|
694
|
-
|
|
695
|
-
- **
|
|
696
|
-
- **
|
|
676
|
+
|
|
677
|
+
- **Allowed characters:** path components must match `/^[a-zA-Z_][a-zA-Z0-9_-]*$|^\d+$/` (identifiers, kebab-case, or array indices).
|
|
678
|
+
- **Column type validation:** only `json` and `jsonb` columns are allowed. Other column types throw an error.
|
|
679
|
+
- **Values:** values are serialized to JSONB literals with proper escaping.
|
|
697
680
|
|
|
698
681
|
> [!NOTE]
|
|
699
682
|
> This feature uses PostgreSQL's `jsonb_set` function. It is only available for columns defined as `json` or `jsonb`.
|
|
700
683
|
|
|
701
|
-
|
|
702
684
|
## ExtraOptions Reference
|
|
703
685
|
|
|
704
|
-
All repository operations accept an `options` parameter with these fields:
|
|
686
|
+
All repository operations accept an `options` parameter (`IExtraOptions`/its postgres narrowing `IDatabaseExtraOptions`) with these fields:
|
|
705
687
|
|
|
706
688
|
| Option | Type | Default | Description |
|
|
707
689
|
|--------|------|---------|-------------|
|
|
708
|
-
| `transaction` | `
|
|
690
|
+
| `transaction` | `IDatabaseTransaction` | - | Transaction context for the operation |
|
|
709
691
|
| `log` | `{ use: boolean; level?: TLogLevel }` | - | Enable operation logging |
|
|
710
692
|
| `shouldSkipDefaultFilter` | `boolean` | `false` | Bypass the default filter from model settings |
|
|
711
693
|
| `lock` | `TLockOptions` | - | Row-level locking (requires transaction, Core API only) |
|
|
@@ -718,7 +700,6 @@ Write operations additionally support:
|
|
|
718
700
|
| `force` | `boolean` | `false` | Allow empty `where` condition on bulk operations |
|
|
719
701
|
| `shouldQueryRange` | `boolean` | `false` | Return `{ data, range }` with total count (find only) |
|
|
720
702
|
|
|
721
|
-
|
|
722
703
|
## Quick Reference
|
|
723
704
|
|
|
724
705
|
| Feature | Code |
|
|
@@ -734,32 +715,15 @@ Write operations additionally support:
|
|
|
734
715
|
| Force delete all | `options: { force: true }` |
|
|
735
716
|
| Skip returning data | `options: { shouldReturn: false }` |
|
|
736
717
|
| Get data + count | `options: { shouldQueryRange: true }` |
|
|
737
|
-
| Access connector | `repository.
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
- [
|
|
743
|
-
- [
|
|
744
|
-
- [
|
|
745
|
-
- [
|
|
746
|
-
- [
|
|
747
|
-
- [
|
|
748
|
-
- [
|
|
749
|
-
- [Array Operators](../filter-system/array-operators) - PostgreSQL arrays
|
|
750
|
-
|
|
751
|
-
## See Also
|
|
752
|
-
|
|
753
|
-
- **Related Concepts:**
|
|
754
|
-
- [Repositories Overview](./index) - Core repository operations
|
|
755
|
-
- [Transactions](/guides/core-concepts/persistent/transactions) - Transaction guide
|
|
756
|
-
- [DataSources](/guides/core-concepts/persistent/datasources) - Database connections
|
|
757
|
-
|
|
758
|
-
- **Related Topics:**
|
|
759
|
-
- [Repository Mixins (Removed)](./mixins) - Where mixin behavior lives now
|
|
760
|
-
- [Relations & Includes](./relations) - Loading related data
|
|
761
|
-
- [Filter System](/references/base/filter-system/) - Query operators
|
|
762
|
-
|
|
763
|
-
- **Best Practices:**
|
|
764
|
-
- [Performance Optimization](/best-practices/performance-optimization) - Query optimization
|
|
765
|
-
- [Data Modeling](/best-practices/data-modeling) - Repository patterns
|
|
718
|
+
| Access connector | `repository.connector` |
|
|
719
|
+
|
|
720
|
+
## See also
|
|
721
|
+
|
|
722
|
+
- [Repositories overview](/references/base/repositories/) - CRUD basics, common tasks
|
|
723
|
+
- [Relations & Includes](./relations) - eager loading, nested `scope` filters, many-to-many
|
|
724
|
+
- [SoftDeletableRepository](./soft-deletable) - soft delete, restore, hard delete
|
|
725
|
+
- [Repository Mixins (Removed)](./mixins) - where `FieldsVisibilityMixin`/`DefaultFilterMixin` behavior lives now
|
|
726
|
+
- [Filter System](/references/base/filter-system/) - every `where` operator, JSON paths, array operators
|
|
727
|
+
- [Default Filter](/references/base/filter-system/default-filter) - automatic filter configuration
|
|
728
|
+
- [DataSources - Full Reference](/references/base/datasources-reference) - transaction internals, isolation levels, driver seam
|
|
729
|
+
- [Transactions guide](/guides/core-concepts/persistent/transactions) - multi-operation database transactions
|