@venizia/ignis-docs 0.0.7-2 → 0.0.8-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mcp-server/common/paths.d.ts +4 -2
- package/dist/mcp-server/common/paths.d.ts.map +1 -1
- package/dist/mcp-server/common/paths.js +8 -6
- package/dist/mcp-server/common/paths.js.map +1 -1
- package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts +1 -1
- package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/docs/get-document-content.tool.js +7 -7
- package/dist/mcp-server/tools/docs/get-document-metadata.tool.js +3 -3
- package/dist/mcp-server/tools/docs/get-package-overview.tool.d.ts +1 -1
- package/dist/mcp-server/tools/docs/get-package-overview.tool.js +1 -1
- package/package.json +1 -1
- package/wiki/best-practices/api-usage-examples.md +9 -9
- package/wiki/best-practices/architectural-patterns.md +19 -3
- package/wiki/best-practices/architecture-decisions.md +6 -6
- package/wiki/best-practices/code-style-standards/advanced-patterns.md +1 -1
- package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
- package/wiki/best-practices/code-style-standards/function-patterns.md +2 -2
- package/wiki/best-practices/code-style-standards/index.md +2 -2
- package/wiki/best-practices/code-style-standards/naming-conventions.md +1 -1
- package/wiki/best-practices/code-style-standards/route-definitions.md +4 -4
- package/wiki/best-practices/data-modeling.md +1 -1
- package/wiki/best-practices/deployment-strategies.md +1 -1
- package/wiki/best-practices/error-handling.md +2 -2
- package/wiki/best-practices/performance-optimization.md +3 -3
- package/wiki/best-practices/security-guidelines.md +2 -2
- package/wiki/best-practices/troubleshooting-tips.md +1 -1
- package/wiki/{references → extensions}/components/authentication/api.md +12 -20
- package/wiki/{references → extensions}/components/authentication/errors.md +1 -1
- package/wiki/{references → extensions}/components/authentication/index.md +5 -8
- package/wiki/{references → extensions}/components/authentication/usage.md +20 -36
- package/wiki/{references → extensions}/components/authorization/api.md +62 -13
- package/wiki/{references → extensions}/components/authorization/errors.md +12 -7
- package/wiki/{references → extensions}/components/authorization/index.md +93 -6
- package/wiki/{references → extensions}/components/authorization/usage.md +42 -4
- package/wiki/{references → extensions}/components/health-check.md +5 -4
- package/wiki/{references → extensions}/components/index.md +2 -0
- package/wiki/{references → extensions}/components/mail/index.md +1 -1
- package/wiki/{references → extensions}/components/request-tracker.md +1 -1
- package/wiki/{references → extensions}/components/socket-io/api.md +2 -2
- package/wiki/{references → extensions}/components/socket-io/errors.md +2 -0
- package/wiki/{references → extensions}/components/socket-io/index.md +24 -20
- package/wiki/{references → extensions}/components/socket-io/usage.md +2 -2
- package/wiki/{references → extensions}/components/static-asset/api.md +14 -15
- package/wiki/{references → extensions}/components/static-asset/errors.md +3 -1
- package/wiki/{references → extensions}/components/static-asset/index.md +158 -89
- package/wiki/{references → extensions}/components/static-asset/usage.md +8 -5
- package/wiki/{references → extensions}/components/swagger.md +3 -3
- package/wiki/{references → extensions}/components/template/index.md +4 -4
- package/wiki/{references → extensions}/components/template/setup-page.md +1 -1
- package/wiki/{references → extensions}/components/template/single-page.md +1 -1
- package/wiki/{references → extensions}/components/websocket/api.md +7 -6
- package/wiki/{references → extensions}/components/websocket/errors.md +17 -3
- package/wiki/{references → extensions}/components/websocket/index.md +17 -11
- package/wiki/{references → extensions}/components/websocket/usage.md +2 -2
- package/wiki/{references → extensions}/helpers/crypto/index.md +1 -1
- package/wiki/{references → extensions}/helpers/env/index.md +9 -5
- package/wiki/{references → extensions}/helpers/error/index.md +2 -7
- package/wiki/{references → extensions}/helpers/index.md +18 -6
- package/wiki/{references → extensions}/helpers/kafka/admin.md +33 -16
- package/wiki/extensions/helpers/kafka/consumer.md +384 -0
- package/wiki/extensions/helpers/kafka/examples.md +361 -0
- package/wiki/extensions/helpers/kafka/index.md +639 -0
- package/wiki/{references → extensions}/helpers/kafka/producer.md +100 -96
- package/wiki/extensions/helpers/kafka/schema-registry.md +214 -0
- package/wiki/{references → extensions}/helpers/logger/index.md +2 -2
- package/wiki/{references → extensions}/helpers/queue/index.md +400 -4
- package/wiki/{references → extensions}/helpers/storage/api.md +170 -10
- package/wiki/{references → extensions}/helpers/storage/index.md +44 -8
- package/wiki/{references → extensions}/helpers/template/index.md +1 -1
- package/wiki/{references → extensions}/helpers/testing/index.md +4 -4
- package/wiki/{references → extensions}/helpers/types/index.md +63 -16
- package/wiki/{references → extensions}/helpers/websocket/index.md +1 -1
- package/wiki/extensions/index.md +48 -0
- package/wiki/guides/core-concepts/application/bootstrapping.md +55 -37
- package/wiki/guides/core-concepts/application/index.md +95 -35
- package/wiki/guides/core-concepts/components-guide.md +23 -19
- package/wiki/guides/core-concepts/components.md +34 -10
- package/wiki/guides/core-concepts/dependency-injection.md +99 -34
- package/wiki/guides/core-concepts/grpc-controllers.md +295 -0
- package/wiki/guides/core-concepts/persistent/datasources.md +27 -8
- package/wiki/guides/core-concepts/persistent/models.md +43 -1
- package/wiki/guides/core-concepts/persistent/repositories.md +75 -8
- package/wiki/guides/core-concepts/persistent/transactions.md +38 -8
- package/wiki/guides/core-concepts/{controllers.md → rest-controllers.md} +30 -33
- package/wiki/guides/core-concepts/services.md +19 -5
- package/wiki/guides/get-started/5-minute-quickstart.md +6 -7
- package/wiki/guides/get-started/philosophy.md +1 -1
- package/wiki/guides/index.md +2 -2
- package/wiki/guides/reference/glossary.md +7 -7
- package/wiki/guides/reference/mcp-docs-server.md +1 -1
- package/wiki/guides/tutorials/building-a-crud-api.md +2 -2
- package/wiki/guides/tutorials/complete-installation.md +17 -14
- package/wiki/guides/tutorials/ecommerce-api.md +18 -18
- package/wiki/guides/tutorials/realtime-chat.md +8 -8
- package/wiki/guides/tutorials/testing.md +2 -2
- package/wiki/index.md +4 -3
- package/wiki/references/base/application.md +341 -21
- package/wiki/references/base/bootstrapping.md +43 -13
- package/wiki/references/base/components.md +259 -8
- package/wiki/references/base/controllers.md +556 -253
- package/wiki/references/base/datasources.md +159 -79
- package/wiki/references/base/dependency-injection.md +299 -48
- package/wiki/references/base/filter-system/application-usage.md +18 -2
- package/wiki/references/base/filter-system/array-operators.md +14 -6
- package/wiki/references/base/filter-system/comparison-operators.md +9 -3
- package/wiki/references/base/filter-system/default-filter.md +28 -3
- package/wiki/references/base/filter-system/fields-order-pagination.md +17 -13
- package/wiki/references/base/filter-system/index.md +169 -11
- package/wiki/references/base/filter-system/json-filtering.md +51 -18
- package/wiki/references/base/filter-system/list-operators.md +4 -3
- package/wiki/references/base/filter-system/logical-operators.md +7 -2
- package/wiki/references/base/filter-system/null-operators.md +50 -0
- package/wiki/references/base/filter-system/quick-reference.md +82 -243
- package/wiki/references/base/filter-system/range-operators.md +7 -1
- package/wiki/references/base/filter-system/tips.md +34 -7
- package/wiki/references/base/filter-system/use-cases.md +6 -5
- package/wiki/references/base/grpc-controllers.md +984 -0
- package/wiki/references/base/index.md +32 -24
- package/wiki/references/base/middleware.md +347 -0
- package/wiki/references/base/models.md +390 -46
- package/wiki/references/base/providers.md +14 -14
- package/wiki/references/base/repositories/advanced.md +84 -69
- package/wiki/references/base/repositories/index.md +447 -12
- package/wiki/references/base/repositories/mixins.md +103 -98
- package/wiki/references/base/repositories/relations.md +129 -45
- package/wiki/references/base/repositories/soft-deletable.md +104 -23
- package/wiki/references/base/services.md +94 -14
- package/wiki/references/index.md +12 -10
- package/wiki/references/quick-reference.md +98 -65
- package/wiki/references/utilities/crypto.md +21 -4
- package/wiki/references/utilities/date.md +25 -7
- package/wiki/references/utilities/index.md +26 -24
- package/wiki/references/utilities/jsx.md +54 -54
- package/wiki/references/utilities/module.md +8 -6
- package/wiki/references/utilities/parse.md +16 -9
- package/wiki/references/utilities/performance.md +22 -7
- package/wiki/references/utilities/promise.md +19 -16
- package/wiki/references/utilities/request.md +48 -26
- package/wiki/references/utilities/schema.md +69 -6
- package/wiki/references/utilities/statuses.md +131 -140
- package/wiki/references/helpers/kafka/consumer.md +0 -473
- package/wiki/references/helpers/kafka/examples.md +0 -234
- package/wiki/references/helpers/kafka/index.md +0 -482
- /package/wiki/{references → extensions}/components/mail/api.md +0 -0
- /package/wiki/{references → extensions}/components/mail/errors.md +0 -0
- /package/wiki/{references → extensions}/components/mail/usage.md +0 -0
- /package/wiki/{references → extensions}/components/template/api-page.md +0 -0
- /package/wiki/{references → extensions}/components/template/errors-page.md +0 -0
- /package/wiki/{references → extensions}/components/template/usage-page.md +0 -0
- /package/wiki/{references → extensions}/helpers/cron/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/inversion/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/network/api.md +0 -0
- /package/wiki/{references → extensions}/helpers/network/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/redis/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/socket-io/api.md +0 -0
- /package/wiki/{references → extensions}/helpers/socket-io/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/template/single-page.md +0 -0
- /package/wiki/{references → extensions}/helpers/uid/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/websocket/api.md +0 -0
- /package/wiki/{references → extensions}/helpers/worker-thread/index.md +0 -0
- /package/wiki/{references → extensions}/src-details/mcp-server.md +0 -0
|
@@ -15,8 +15,10 @@ A repository that overrides delete operations to set a `deletedAt` timestamp ins
|
|
|
15
15
|
|
|
16
16
|
### 1. Define Model with Soft Delete
|
|
17
17
|
|
|
18
|
+
Use the `generateTzColumnDefs` enricher with the `deleted` option enabled to add a `deletedAt` column:
|
|
19
|
+
|
|
18
20
|
```typescript
|
|
19
|
-
import { pgTable, text
|
|
21
|
+
import { pgTable, text } from 'drizzle-orm/pg-core';
|
|
20
22
|
import {
|
|
21
23
|
BaseEntity,
|
|
22
24
|
model,
|
|
@@ -34,17 +36,19 @@ import {
|
|
|
34
36
|
export class Category extends BaseEntity<typeof Category.schema> {
|
|
35
37
|
static override schema = pgTable('Category', {
|
|
36
38
|
...generateIdColumnDefs({ id: { dataType: 'string' } }),
|
|
37
|
-
...generateTzColumnDefs(
|
|
39
|
+
...generateTzColumnDefs({
|
|
40
|
+
deleted: { enable: true, columnName: 'deleted_at', withTimezone: true },
|
|
41
|
+
}),
|
|
38
42
|
name: text('name').notNull(),
|
|
39
|
-
deletedAt: timestamp('deleted_at', { mode: 'date', withTimezone: true }),
|
|
40
43
|
});
|
|
41
44
|
}
|
|
42
45
|
```
|
|
43
46
|
|
|
44
47
|
> [!IMPORTANT]
|
|
45
|
-
> - The model **must** have a `deletedAt` column (`Date | null`).
|
|
46
|
-
> - Set `defaultFilter: { where: { deletedAt: null } }` so soft-deleted records are excluded by default.
|
|
48
|
+
> - The model **must** have a `deletedAt` column (`Date | null`). The `SoftDeletableRepository` requires `TSoftDeletableTableSchema` which enforces `{ deletedAt: AnyPgColumn<{ data: Date | null }> }`.
|
|
49
|
+
> - Set `defaultFilter: { where: { deletedAt: null } }` in `@model` settings so soft-deleted records are excluded by default.
|
|
47
50
|
> - Optionally add `deletedAt` to `hiddenProperties` to hide it from API responses.
|
|
51
|
+
> - Use `generateTzColumnDefs` with `deleted: { enable: true, ... }` to add the column, or define it manually with `timestamp('deleted_at', { mode: 'date', withTimezone: true })`.
|
|
48
52
|
|
|
49
53
|
### 2. Create Repository
|
|
50
54
|
|
|
@@ -60,16 +64,22 @@ export class CategoryRepository extends SoftDeletableRepository<typeof Category.
|
|
|
60
64
|
|
|
61
65
|
## Delete Operations
|
|
62
66
|
|
|
63
|
-
All delete methods set `deletedAt = new Date()` instead of removing the row. They internally call the corresponding `update` method.
|
|
67
|
+
All delete methods set `deletedAt = new Date()` instead of removing the row. They internally call the corresponding `update` method (`updateById` or `updateAll`).
|
|
64
68
|
|
|
65
69
|
### deleteById
|
|
66
70
|
|
|
67
71
|
```typescript
|
|
68
|
-
// Soft delete
|
|
72
|
+
// Soft delete - sets deletedAt timestamp
|
|
69
73
|
const result = await repo.deleteById({ id: '123' });
|
|
70
74
|
// { count: 1, data: { id: '123', name: 'Electronics', deletedAt: '2026-03-06T...' } }
|
|
71
75
|
|
|
72
|
-
//
|
|
76
|
+
// Without returning data
|
|
77
|
+
const result = await repo.deleteById({
|
|
78
|
+
id: '123',
|
|
79
|
+
options: { shouldReturn: false },
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Hard delete - physically removes the row
|
|
73
83
|
const result = await repo.deleteById({
|
|
74
84
|
id: '123',
|
|
75
85
|
options: { shouldHardDelete: true },
|
|
@@ -95,10 +105,16 @@ const result = await repo.deleteAll({
|
|
|
95
105
|
### deleteBy
|
|
96
106
|
|
|
97
107
|
```typescript
|
|
98
|
-
// Soft delete by where condition (
|
|
108
|
+
// Soft delete by where condition (alias for deleteAll)
|
|
99
109
|
const result = await repo.deleteBy({
|
|
100
110
|
where: { name: 'Obsolete' },
|
|
101
111
|
});
|
|
112
|
+
|
|
113
|
+
// Hard delete by where condition
|
|
114
|
+
const result = await repo.deleteBy({
|
|
115
|
+
where: { name: 'Obsolete' },
|
|
116
|
+
options: { shouldHardDelete: true },
|
|
117
|
+
});
|
|
102
118
|
```
|
|
103
119
|
|
|
104
120
|
|
|
@@ -137,7 +153,7 @@ const result = await repo.restoreAll({
|
|
|
137
153
|
### restoreBy
|
|
138
154
|
|
|
139
155
|
```typescript
|
|
140
|
-
// Alias for restoreAll
|
|
156
|
+
// Alias for restoreAll
|
|
141
157
|
const result = await repo.restoreBy({
|
|
142
158
|
where: { status: 'archived' },
|
|
143
159
|
});
|
|
@@ -148,7 +164,7 @@ const result = await repo.restoreBy({
|
|
|
148
164
|
|
|
149
165
|
### findById with isStrict
|
|
150
166
|
|
|
151
|
-
`SoftDeletableRepository` overrides `findById` to support
|
|
167
|
+
`SoftDeletableRepository` overrides `findById` to support an `isStrict` option that throws a `404 Not Found` error when the record doesn't exist:
|
|
152
168
|
|
|
153
169
|
```typescript
|
|
154
170
|
// Returns null if not found (default)
|
|
@@ -159,8 +175,11 @@ const category = await repo.findById({
|
|
|
159
175
|
id: '123',
|
|
160
176
|
options: { isStrict: true },
|
|
161
177
|
});
|
|
178
|
+
// Throws: [CategoryRepository][findById] Entity with id 123 not found (HTTP 404)
|
|
162
179
|
```
|
|
163
180
|
|
|
181
|
+
All other read operations (`find`, `findOne`, `count`, `existsWith`) work as normal. The default filter (`{ deletedAt: null }`) automatically excludes soft-deleted records. Use `shouldSkipDefaultFilter: true` to include them.
|
|
182
|
+
|
|
164
183
|
|
|
165
184
|
## Options Reference
|
|
166
185
|
|
|
@@ -170,30 +189,36 @@ const category = await repo.findById({
|
|
|
170
189
|
|--------|------|---------|-------------|
|
|
171
190
|
| `shouldHardDelete` | `boolean` | `false` | Bypass soft delete and physically remove the row |
|
|
172
191
|
| `shouldReturn` | `boolean` | `true` | Return the updated/deleted record |
|
|
173
|
-
| `force` | `boolean` | `false` | Allow empty `where` condition (deleteAll
|
|
174
|
-
| `transaction` | `ITransaction` |
|
|
192
|
+
| `force` | `boolean` | `false` | Allow empty `where` condition (`deleteAll`/`deleteBy`) |
|
|
193
|
+
| `transaction` | `ITransaction` | - | Transaction context |
|
|
194
|
+
| `log` | `{ use: boolean; level?: TLogLevel }` | - | Enable operation logging |
|
|
195
|
+
| `shouldSkipDefaultFilter` | `boolean` | `false` | Bypass the default filter |
|
|
175
196
|
|
|
176
197
|
### Restore Options
|
|
177
198
|
|
|
178
199
|
| Option | Type | Default | Description |
|
|
179
200
|
|--------|------|---------|-------------|
|
|
180
201
|
| `shouldReturn` | `boolean` | `true` | Return the restored record |
|
|
181
|
-
| `force` | `boolean` | `false` | Allow empty `where` condition (restoreAll) |
|
|
182
|
-
| `transaction` | `ITransaction` |
|
|
202
|
+
| `force` | `boolean` | `false` | Allow empty `where` condition (`restoreAll`) |
|
|
203
|
+
| `transaction` | `ITransaction` | - | Transaction context |
|
|
204
|
+
|
|
205
|
+
> [!NOTE]
|
|
206
|
+
> Restore operations automatically set `shouldSkipDefaultFilter: true` internally so they can find soft-deleted records that the default filter would normally hide. You do not need to set this yourself.
|
|
183
207
|
|
|
184
208
|
|
|
185
209
|
## How It Works
|
|
186
210
|
|
|
187
|
-
| Operation | Behavior |
|
|
188
|
-
|
|
189
|
-
| `deleteById` | `UPDATE SET deletedAt = NOW() WHERE id = ?` |
|
|
190
|
-
| `deleteAll` | `UPDATE SET deletedAt = NOW() WHERE ...` |
|
|
191
|
-
| `restoreById` | `UPDATE SET deletedAt = NULL WHERE id = ?` (
|
|
192
|
-
| `restoreAll` | `UPDATE SET deletedAt = NULL WHERE ...` (
|
|
193
|
-
| `find` / `findOne` | Default filter automatically
|
|
211
|
+
| Operation | SQL Behavior |
|
|
212
|
+
|-----------|-------------|
|
|
213
|
+
| `deleteById` | `UPDATE SET deletedAt = NOW() WHERE id = ?` (via `updateById`) |
|
|
214
|
+
| `deleteAll` / `deleteBy` | `UPDATE SET deletedAt = NOW() WHERE ...` (via `updateAll`) |
|
|
215
|
+
| `restoreById` | `UPDATE SET deletedAt = NULL WHERE id = ?` (via `updateById` with `shouldSkipDefaultFilter: true`) |
|
|
216
|
+
| `restoreAll` / `restoreBy` | `UPDATE SET deletedAt = NULL WHERE ...` (via `updateAll` with `shouldSkipDefaultFilter: true`) |
|
|
217
|
+
| `find` / `findOne` / `count` | Default filter automatically adds `WHERE deletedAt IS NULL` |
|
|
218
|
+
| `deleteById({ shouldHardDelete: true })` | `DELETE FROM ... WHERE id = ?` (delegates to parent `PersistableRepository`) |
|
|
194
219
|
|
|
195
220
|
> [!TIP]
|
|
196
|
-
>
|
|
221
|
+
> The `shouldHardDelete` option bypasses soft delete entirely and delegates to the parent `DefaultCRUDRepository`'s delete implementation, which performs a real SQL `DELETE`.
|
|
197
222
|
|
|
198
223
|
|
|
199
224
|
## With Transactions
|
|
@@ -211,3 +236,59 @@ try {
|
|
|
211
236
|
await tx.rollback();
|
|
212
237
|
}
|
|
213
238
|
```
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
## Type Constraint
|
|
242
|
+
|
|
243
|
+
`SoftDeletableRepository` enforces that the schema includes a `deletedAt` column at the type level:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
export type TSoftDeletableTableSchema = TTableSchemaWithId & {
|
|
247
|
+
deletedAt: AnyPgColumn<{ data: Date | null }>;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
export class SoftDeletableRepository<
|
|
251
|
+
EntitySchema extends TSoftDeletableTableSchema = TSoftDeletableTableSchema,
|
|
252
|
+
// ...
|
|
253
|
+
> extends DefaultCRUDRepository<EntitySchema, ...> { }
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
If your schema does not have a `deletedAt` column, you will get a TypeScript compilation error when extending `SoftDeletableRepository`.
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
## Class Hierarchy
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
AbstractRepository
|
|
263
|
+
-> ReadableRepository
|
|
264
|
+
-> PersistableRepository
|
|
265
|
+
-> DefaultCRUDRepository
|
|
266
|
+
-> SoftDeletableRepository <-- you are here
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
## Quick Reference
|
|
271
|
+
|
|
272
|
+
| Want to... | Code |
|
|
273
|
+
|------------|------|
|
|
274
|
+
| Soft delete by ID | `repo.deleteById({ id })` |
|
|
275
|
+
| Hard delete by ID | `repo.deleteById({ id, options: { shouldHardDelete: true } })` |
|
|
276
|
+
| Soft delete by condition | `repo.deleteAll({ where, options: { force: true } })` |
|
|
277
|
+
| Restore by ID | `repo.restoreById({ id })` |
|
|
278
|
+
| Restore by condition | `repo.restoreAll({ where })` |
|
|
279
|
+
| Find including deleted | `repo.find({ filter, options: { shouldSkipDefaultFilter: true } })` |
|
|
280
|
+
| Strict findById (404) | `repo.findById({ id, options: { isStrict: true } })` |
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
## Next Steps
|
|
284
|
+
|
|
285
|
+
- [Advanced Features](./advanced.md) - Transactions, hidden properties
|
|
286
|
+
- [Repository Mixins](./mixins.md) - Default filter and fields visibility
|
|
287
|
+
- [Repository Overview](./index.md) - Repository basics
|
|
288
|
+
|
|
289
|
+
## See Also
|
|
290
|
+
|
|
291
|
+
- **Related Concepts:**
|
|
292
|
+
- [Repositories Overview](./index) - Core repository operations
|
|
293
|
+
- [Default Filter](../filter-system/default-filter) - Automatic filtering
|
|
294
|
+
- [Models](/guides/core-concepts/persistent/models) - Entity definitions with enrichers
|
|
@@ -17,34 +17,69 @@ Technical reference for `BaseService` - the foundation for business logic layers
|
|
|
17
17
|
| **Extends `BaseHelper`** | Auto-configured scoped logger (`this.logger`) |
|
|
18
18
|
| **DI Integration** | Fits into framework's dependency injection system |
|
|
19
19
|
| **Business Logic Layer** | Bridge between Controllers and Repositories |
|
|
20
|
+
| **No built-in CRUD** | Services are for business logic, not data access — that's what Repositories are for |
|
|
20
21
|
|
|
21
22
|
## `BaseService` Class
|
|
22
23
|
|
|
23
|
-
Abstract class that all application services should extend.
|
|
24
|
+
Abstract class that all application services should extend. It implements the `IService` interface (currently a marker interface with no required methods).
|
|
25
|
+
|
|
26
|
+
### Class Definition
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { BaseHelper } from '@venizia/ignis-helpers';
|
|
30
|
+
import { IService } from './types';
|
|
31
|
+
|
|
32
|
+
export abstract class BaseService extends BaseHelper implements IService {
|
|
33
|
+
constructor(opts: { scope: string }) {
|
|
34
|
+
super({ scope: opts.scope });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
24
38
|
|
|
25
39
|
### Key Features
|
|
26
40
|
|
|
27
41
|
| Feature | Description |
|
|
28
42
|
| :--- | :--- |
|
|
29
43
|
| **Standardization** | Common base for all services, fits framework architecture |
|
|
30
|
-
| **Logging** | Extends `BaseHelper` - auto-configured logger at `this.logger` (scope = class name) |
|
|
44
|
+
| **Logging** | Extends `BaseHelper` from `@venizia/ignis-helpers` — auto-configured logger at `this.logger` (scope = class name) |
|
|
31
45
|
| **Clarity** | Signals the class contains business logic |
|
|
32
46
|
|
|
33
|
-
###
|
|
47
|
+
### Constructor
|
|
34
48
|
|
|
35
|
-
The
|
|
49
|
+
The constructor requires an options object with a `scope` string, which is typically set to the class name:
|
|
36
50
|
|
|
37
51
|
```typescript
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
export abstract class BaseService extends BaseHelper implements IService {
|
|
42
|
-
constructor(opts: { scope: string }) {
|
|
43
|
-
super({ scope: opts.scope });
|
|
52
|
+
class UserService extends BaseService {
|
|
53
|
+
constructor() {
|
|
54
|
+
super({ scope: UserService.name });
|
|
44
55
|
}
|
|
45
56
|
}
|
|
46
57
|
```
|
|
47
58
|
|
|
59
|
+
## `IService` Interface
|
|
60
|
+
|
|
61
|
+
The `IService` interface is a marker interface with no required methods. It exists to provide a type-level contract for services.
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
export interface IService {}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## No Built-in CRUD Service
|
|
68
|
+
|
|
69
|
+
Ignis intentionally does not provide a `BaseCrudService`. CRUD operations belong in the Repository layer (`DefaultCRUDRepository`). Services are for business logic that orchestrates one or more repositories, performs validation, handles transactions, or coordinates cross-cutting concerns.
|
|
70
|
+
|
|
71
|
+
## Registration
|
|
72
|
+
|
|
73
|
+
Services are registered with the DI container using the `app.service()` method or via the boot system's auto-discovery:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// Manual registration (in preConfigure or registerComponents)
|
|
77
|
+
app.service(UserService); // Binds as 'services.UserService'
|
|
78
|
+
|
|
79
|
+
// Or via boot system auto-discovery:
|
|
80
|
+
// Place file at src/services/user.service.ts → auto-discovered and bound
|
|
81
|
+
```
|
|
82
|
+
|
|
48
83
|
## How Services Fit into the Architecture
|
|
49
84
|
|
|
50
85
|
Services are the core of your application's logic. They act as a bridge between the presentation layer (Controllers) and the data access layer (Repositories).
|
|
@@ -64,12 +99,12 @@ Services are the core of your application's logic. They act as a bridge between
|
|
|
64
99
|
### Example
|
|
65
100
|
|
|
66
101
|
```typescript
|
|
67
|
-
import { BaseService, inject } from '@venizia/ignis';
|
|
102
|
+
import { BaseService, inject, injectable } from '@venizia/ignis';
|
|
68
103
|
import { getError } from '@venizia/ignis-helpers';
|
|
69
104
|
import { UserRepository } from '../repositories/user.repository';
|
|
70
105
|
import { TUser } from '../models/entities';
|
|
71
106
|
|
|
72
|
-
// 1. Service is decorated with
|
|
107
|
+
// 1. Service is decorated with @injectable (or registered via app.service())
|
|
73
108
|
@injectable()
|
|
74
109
|
export class UserService extends BaseService {
|
|
75
110
|
// 2. Dependencies (like UserRepository) are injected
|
|
@@ -94,15 +129,60 @@ export class UserService extends BaseService {
|
|
|
94
129
|
// 5. Returns transformed data
|
|
95
130
|
return {
|
|
96
131
|
id: user.id,
|
|
97
|
-
name: user.name,
|
|
132
|
+
name: user.name,
|
|
98
133
|
email: user.email,
|
|
99
134
|
};
|
|
100
135
|
}
|
|
101
136
|
}
|
|
102
137
|
```
|
|
103
138
|
|
|
104
|
-
|
|
139
|
+
### Transaction Orchestration
|
|
140
|
+
|
|
141
|
+
A common service pattern is orchestrating transactions across multiple repositories:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
@injectable()
|
|
145
|
+
export class OrderService extends BaseService {
|
|
146
|
+
constructor(
|
|
147
|
+
@inject({ key: 'repositories.OrderRepository' })
|
|
148
|
+
private orderRepo: OrderRepository,
|
|
149
|
+
|
|
150
|
+
@inject({ key: 'repositories.InventoryRepository' })
|
|
151
|
+
private inventoryRepo: InventoryRepository,
|
|
152
|
+
|
|
153
|
+
@inject({ key: 'datasources.PostgresDataSource' })
|
|
154
|
+
private dataSource: PostgresDataSource,
|
|
155
|
+
) {
|
|
156
|
+
super({ scope: OrderService.name });
|
|
157
|
+
}
|
|
105
158
|
|
|
159
|
+
async placeOrder(opts: { userId: string; items: OrderItem[] }) {
|
|
160
|
+
const transaction = await this.dataSource.beginTransaction();
|
|
161
|
+
try {
|
|
162
|
+
const order = await this.orderRepo.create({
|
|
163
|
+
data: { userId: opts.userId, items: opts.items },
|
|
164
|
+
options: { transaction },
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
for (const item of opts.items) {
|
|
168
|
+
await this.inventoryRepo.updateById({
|
|
169
|
+
id: item.productId,
|
|
170
|
+
data: { quantity: item.quantity },
|
|
171
|
+
options: { transaction },
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
await transaction.commit();
|
|
176
|
+
return order;
|
|
177
|
+
} catch (error) {
|
|
178
|
+
await transaction.rollback();
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
By adhering to this pattern, you keep your code organized, testable, and maintainable. You can easily test `UserService` by providing a mock `UserRepository` without needing a real database connection.
|
|
106
186
|
|
|
107
187
|
## See Also
|
|
108
188
|
|
package/wiki/references/index.md
CHANGED
|
@@ -9,12 +9,12 @@ Complete reference documentation for the Ignis framework. Find detailed API docs
|
|
|
9
9
|
<p>Application, Controller, Service, Repository, Model</p>
|
|
10
10
|
</a>
|
|
11
11
|
|
|
12
|
-
<a href="
|
|
12
|
+
<a href="/ignis/extensions/components/" class="guide-card">
|
|
13
13
|
<h3>Components</h3>
|
|
14
14
|
<p>Auth, Mail, Socket.IO, Swagger, Health Check</p>
|
|
15
15
|
</a>
|
|
16
16
|
|
|
17
|
-
<a href="
|
|
17
|
+
<a href="/ignis/extensions/helpers/" class="guide-card">
|
|
18
18
|
<h3>Helpers</h3>
|
|
19
19
|
<p>Logger, Redis, Queue, Storage, Cron, Crypto</p>
|
|
20
20
|
</a>
|
|
@@ -29,7 +29,7 @@ Complete reference documentation for the Ignis framework. Find detailed API docs
|
|
|
29
29
|
<p>Environment variables and settings</p>
|
|
30
30
|
</a>
|
|
31
31
|
|
|
32
|
-
<a href="
|
|
32
|
+
<a href="/ignis/extensions/src-details/" class="guide-card">
|
|
33
33
|
<h3>Framework Internals</h3>
|
|
34
34
|
<p>Package structure and architecture</p>
|
|
35
35
|
</a>
|
|
@@ -63,7 +63,7 @@ Complete reference documentation for the Ignis framework. Find detailed API docs
|
|
|
63
63
|
<span class="stage-num">3</span>
|
|
64
64
|
<h4>Adding Features</h4>
|
|
65
65
|
</div>
|
|
66
|
-
<p><a href="
|
|
66
|
+
<p><a href="/ignis/extensions/components/authentication/">Auth</a> → <a href="/ignis/extensions/components/socket-io/">Real-time</a> → <a href="/ignis/extensions/components/mail/">Email</a> → <a href="/ignis/extensions/components/swagger">API Docs</a></p>
|
|
67
67
|
<span class="stage-desc">Pre-built components for common features</span>
|
|
68
68
|
</div>
|
|
69
69
|
|
|
@@ -72,7 +72,7 @@ Complete reference documentation for the Ignis framework. Find detailed API docs
|
|
|
72
72
|
<span class="stage-num">4</span>
|
|
73
73
|
<h4>Infrastructure</h4>
|
|
74
74
|
</div>
|
|
75
|
-
<p><a href="
|
|
75
|
+
<p><a href="/ignis/extensions/helpers/logger/">Logging</a> → <a href="/ignis/extensions/helpers/redis/">Caching</a> → <a href="/ignis/extensions/helpers/queue/">Queues</a> → <a href="/ignis/extensions/helpers/cron/">Scheduling</a></p>
|
|
76
76
|
<span class="stage-desc">Background jobs, caching, and observability</span>
|
|
77
77
|
</div>
|
|
78
78
|
|
|
@@ -90,7 +90,7 @@ Complete reference documentation for the Ignis framework. Find detailed API docs
|
|
|
90
90
|
<span class="stage-num">6</span>
|
|
91
91
|
<h4>Production Ready</h4>
|
|
92
92
|
</div>
|
|
93
|
-
<p><a href="./base/middlewares">Error Handling</a> → <a href="
|
|
93
|
+
<p><a href="./base/middlewares">Error Handling</a> → <a href="/ignis/extensions/components/health-check">Health Checks</a> → <a href="./configuration/environment-variables">Configuration</a> → <a href="./base/bootstrapping">Auto-Discovery</a></p>
|
|
94
94
|
<span class="stage-desc">Production deployment, monitoring, and reliability</span>
|
|
95
95
|
</div>
|
|
96
96
|
|
|
@@ -99,7 +99,7 @@ Complete reference documentation for the Ignis framework. Find detailed API docs
|
|
|
99
99
|
<span class="stage-num">7</span>
|
|
100
100
|
<h4>Testing & Quality</h4>
|
|
101
101
|
</div>
|
|
102
|
-
<p><a href="
|
|
102
|
+
<p><a href="/ignis/extensions/helpers/testing/">Unit Testing</a> → <a href="./base/repositories/advanced">Mocking & Stubs</a> → <a href="./quick-reference">Best Practices</a></p>
|
|
103
103
|
<span class="stage-desc">Testing strategies, quality assurance, and code review</span>
|
|
104
104
|
</div>
|
|
105
105
|
|
|
@@ -113,7 +113,7 @@ Complete reference documentation for the Ignis framework. Find detailed API docs
|
|
|
113
113
|
**Define a Controller:**
|
|
114
114
|
```typescript
|
|
115
115
|
@controller({ path: '/users' })
|
|
116
|
-
class UserController extends
|
|
116
|
+
class UserController extends BaseRestController {
|
|
117
117
|
@get({ configs: { path: '/:id' } })
|
|
118
118
|
getUser(c: Context) {
|
|
119
119
|
return c.json({ id: c.req.param('id') });
|
|
@@ -143,11 +143,13 @@ CronHelper.schedule('0 * * * *', async () => {
|
|
|
143
143
|
// Core framework
|
|
144
144
|
import {
|
|
145
145
|
BaseApplication,
|
|
146
|
-
|
|
146
|
+
BaseRestController,
|
|
147
|
+
BaseGrpcController,
|
|
147
148
|
BaseService,
|
|
148
|
-
|
|
149
|
+
DefaultCRUDRepository,
|
|
149
150
|
controller,
|
|
150
151
|
get, post, put, del,
|
|
152
|
+
rpc, unary, serverStream,
|
|
151
153
|
inject,
|
|
152
154
|
} from '@venizia/ignis';
|
|
153
155
|
|