@venizia/ignis-docs 0.0.7 → 0.0.8-1

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 (172) hide show
  1. package/dist/mcp-server/common/paths.d.ts +4 -2
  2. package/dist/mcp-server/common/paths.d.ts.map +1 -1
  3. package/dist/mcp-server/common/paths.js +8 -6
  4. package/dist/mcp-server/common/paths.js.map +1 -1
  5. package/dist/mcp-server/helpers/docs.helper.d.ts.map +1 -1
  6. package/dist/mcp-server/helpers/docs.helper.js +1 -1
  7. package/dist/mcp-server/helpers/docs.helper.js.map +1 -1
  8. package/dist/mcp-server/tools/base.tool.d.ts +1 -1
  9. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts +1 -1
  10. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts.map +1 -1
  11. package/dist/mcp-server/tools/docs/get-document-content.tool.js +7 -7
  12. package/dist/mcp-server/tools/docs/get-document-metadata.tool.js +3 -3
  13. package/dist/mcp-server/tools/docs/get-package-overview.tool.d.ts +1 -1
  14. package/dist/mcp-server/tools/docs/get-package-overview.tool.js +1 -1
  15. package/dist/mcp-server/tools/docs/search-documents.tool.d.ts +1 -1
  16. package/dist/mcp-server/tools/docs/search-documents.tool.js +1 -1
  17. package/dist/mcp-server/tools/docs/search-documents.tool.js.map +1 -1
  18. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  19. package/dist/mcp-server/tools/github/list-project-files.tool.js +1 -1
  20. package/dist/mcp-server/tools/github/list-project-files.tool.js.map +1 -1
  21. package/dist/mcp-server/tools/github/search-code.tool.d.ts +1 -1
  22. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  23. package/dist/mcp-server/tools/github/search-code.tool.js.map +1 -1
  24. package/package.json +9 -9
  25. package/wiki/best-practices/api-usage-examples.md +9 -9
  26. package/wiki/best-practices/architectural-patterns.md +19 -3
  27. package/wiki/best-practices/architecture-decisions.md +6 -6
  28. package/wiki/best-practices/code-style-standards/advanced-patterns.md +1 -1
  29. package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
  30. package/wiki/best-practices/code-style-standards/function-patterns.md +2 -2
  31. package/wiki/best-practices/code-style-standards/index.md +2 -2
  32. package/wiki/best-practices/code-style-standards/naming-conventions.md +1 -1
  33. package/wiki/best-practices/code-style-standards/route-definitions.md +4 -4
  34. package/wiki/best-practices/data-modeling.md +1 -1
  35. package/wiki/best-practices/deployment-strategies.md +1 -1
  36. package/wiki/best-practices/error-handling.md +2 -2
  37. package/wiki/best-practices/performance-optimization.md +3 -3
  38. package/wiki/best-practices/security-guidelines.md +2 -2
  39. package/wiki/best-practices/troubleshooting-tips.md +1 -1
  40. package/wiki/{references → extensions}/components/authentication/api.md +12 -20
  41. package/wiki/{references → extensions}/components/authentication/errors.md +1 -1
  42. package/wiki/{references → extensions}/components/authentication/index.md +5 -8
  43. package/wiki/{references → extensions}/components/authentication/usage.md +20 -36
  44. package/wiki/{references → extensions}/components/authorization/api.md +62 -13
  45. package/wiki/{references → extensions}/components/authorization/errors.md +12 -7
  46. package/wiki/{references → extensions}/components/authorization/index.md +93 -6
  47. package/wiki/{references → extensions}/components/authorization/usage.md +42 -4
  48. package/wiki/{references → extensions}/components/health-check.md +5 -4
  49. package/wiki/{references → extensions}/components/index.md +2 -0
  50. package/wiki/{references → extensions}/components/mail/index.md +1 -1
  51. package/wiki/{references → extensions}/components/request-tracker.md +1 -1
  52. package/wiki/{references → extensions}/components/socket-io/api.md +2 -2
  53. package/wiki/{references → extensions}/components/socket-io/errors.md +2 -0
  54. package/wiki/{references → extensions}/components/socket-io/index.md +24 -20
  55. package/wiki/{references → extensions}/components/socket-io/usage.md +2 -2
  56. package/wiki/{references → extensions}/components/static-asset/api.md +14 -15
  57. package/wiki/{references → extensions}/components/static-asset/errors.md +3 -1
  58. package/wiki/{references → extensions}/components/static-asset/index.md +158 -89
  59. package/wiki/{references → extensions}/components/static-asset/usage.md +8 -5
  60. package/wiki/{references → extensions}/components/swagger.md +3 -3
  61. package/wiki/{references → extensions}/components/template/index.md +4 -4
  62. package/wiki/{references → extensions}/components/template/setup-page.md +1 -1
  63. package/wiki/{references → extensions}/components/template/single-page.md +1 -1
  64. package/wiki/{references → extensions}/components/websocket/api.md +7 -6
  65. package/wiki/{references → extensions}/components/websocket/errors.md +17 -3
  66. package/wiki/{references → extensions}/components/websocket/index.md +17 -11
  67. package/wiki/{references → extensions}/components/websocket/usage.md +2 -2
  68. package/wiki/{references → extensions}/helpers/crypto/index.md +1 -1
  69. package/wiki/{references → extensions}/helpers/env/index.md +9 -5
  70. package/wiki/{references → extensions}/helpers/error/index.md +2 -7
  71. package/wiki/{references → extensions}/helpers/index.md +18 -6
  72. package/wiki/{references → extensions}/helpers/kafka/admin.md +13 -1
  73. package/wiki/{references → extensions}/helpers/kafka/consumer.md +32 -31
  74. package/wiki/{references → extensions}/helpers/kafka/examples.md +20 -20
  75. package/wiki/{references → extensions}/helpers/kafka/index.md +61 -54
  76. package/wiki/{references → extensions}/helpers/kafka/producer.md +21 -20
  77. package/wiki/{references → extensions}/helpers/kafka/schema-registry.md +25 -25
  78. package/wiki/{references → extensions}/helpers/logger/index.md +2 -2
  79. package/wiki/{references → extensions}/helpers/queue/index.md +400 -4
  80. package/wiki/{references → extensions}/helpers/storage/api.md +170 -10
  81. package/wiki/{references → extensions}/helpers/storage/index.md +44 -8
  82. package/wiki/{references → extensions}/helpers/template/index.md +1 -1
  83. package/wiki/{references → extensions}/helpers/testing/index.md +4 -4
  84. package/wiki/{references → extensions}/helpers/types/index.md +63 -16
  85. package/wiki/{references → extensions}/helpers/websocket/index.md +1 -1
  86. package/wiki/extensions/index.md +48 -0
  87. package/wiki/guides/core-concepts/application/bootstrapping.md +55 -37
  88. package/wiki/guides/core-concepts/application/index.md +95 -35
  89. package/wiki/guides/core-concepts/components-guide.md +23 -19
  90. package/wiki/guides/core-concepts/components.md +34 -10
  91. package/wiki/guides/core-concepts/dependency-injection.md +99 -34
  92. package/wiki/guides/core-concepts/grpc-controllers.md +295 -0
  93. package/wiki/guides/core-concepts/persistent/datasources.md +37 -19
  94. package/wiki/guides/core-concepts/persistent/index.md +6 -6
  95. package/wiki/guides/core-concepts/persistent/models.md +50 -6
  96. package/wiki/guides/core-concepts/persistent/repositories.md +83 -8
  97. package/wiki/guides/core-concepts/persistent/transactions.md +39 -8
  98. package/wiki/guides/core-concepts/{controllers.md → rest-controllers.md} +32 -35
  99. package/wiki/guides/core-concepts/services.md +19 -6
  100. package/wiki/guides/get-started/5-minute-quickstart.md +17 -17
  101. package/wiki/guides/get-started/philosophy.md +1 -1
  102. package/wiki/guides/index.md +2 -2
  103. package/wiki/guides/reference/glossary.md +7 -7
  104. package/wiki/guides/reference/mcp-docs-server.md +1 -1
  105. package/wiki/guides/tutorials/building-a-crud-api.md +45 -39
  106. package/wiki/guides/tutorials/complete-installation.md +74 -51
  107. package/wiki/guides/tutorials/ecommerce-api.md +39 -30
  108. package/wiki/guides/tutorials/realtime-chat.md +12 -13
  109. package/wiki/guides/tutorials/testing.md +2 -2
  110. package/wiki/index.md +4 -3
  111. package/wiki/references/base/application.md +341 -21
  112. package/wiki/references/base/bootstrapping.md +43 -13
  113. package/wiki/references/base/components.md +259 -8
  114. package/wiki/references/base/controllers.md +556 -253
  115. package/wiki/references/base/datasources.md +159 -79
  116. package/wiki/references/base/dependency-injection.md +299 -48
  117. package/wiki/references/base/filter-system/application-usage.md +18 -2
  118. package/wiki/references/base/filter-system/array-operators.md +14 -6
  119. package/wiki/references/base/filter-system/comparison-operators.md +9 -3
  120. package/wiki/references/base/filter-system/default-filter.md +28 -3
  121. package/wiki/references/base/filter-system/fields-order-pagination.md +17 -13
  122. package/wiki/references/base/filter-system/index.md +169 -11
  123. package/wiki/references/base/filter-system/json-filtering.md +51 -18
  124. package/wiki/references/base/filter-system/list-operators.md +4 -3
  125. package/wiki/references/base/filter-system/logical-operators.md +7 -2
  126. package/wiki/references/base/filter-system/null-operators.md +50 -0
  127. package/wiki/references/base/filter-system/quick-reference.md +82 -243
  128. package/wiki/references/base/filter-system/range-operators.md +7 -1
  129. package/wiki/references/base/filter-system/tips.md +34 -7
  130. package/wiki/references/base/filter-system/use-cases.md +6 -5
  131. package/wiki/references/base/grpc-controllers.md +984 -0
  132. package/wiki/references/base/index.md +32 -24
  133. package/wiki/references/base/middleware.md +347 -0
  134. package/wiki/references/base/models.md +390 -46
  135. package/wiki/references/base/providers.md +14 -14
  136. package/wiki/references/base/repositories/advanced.md +195 -69
  137. package/wiki/references/base/repositories/index.md +447 -12
  138. package/wiki/references/base/repositories/mixins.md +103 -98
  139. package/wiki/references/base/repositories/relations.md +129 -45
  140. package/wiki/references/base/repositories/soft-deletable.md +104 -23
  141. package/wiki/references/base/services.md +94 -14
  142. package/wiki/references/index.md +12 -10
  143. package/wiki/references/quick-reference.md +98 -65
  144. package/wiki/references/utilities/crypto.md +21 -4
  145. package/wiki/references/utilities/date.md +25 -7
  146. package/wiki/references/utilities/index.md +26 -24
  147. package/wiki/references/utilities/jsx.md +54 -54
  148. package/wiki/references/utilities/module.md +8 -6
  149. package/wiki/references/utilities/parse.md +16 -9
  150. package/wiki/references/utilities/performance.md +22 -7
  151. package/wiki/references/utilities/promise.md +19 -16
  152. package/wiki/references/utilities/request.md +48 -26
  153. package/wiki/references/utilities/schema.md +69 -6
  154. package/wiki/references/utilities/statuses.md +131 -140
  155. /package/wiki/{references → extensions}/components/mail/api.md +0 -0
  156. /package/wiki/{references → extensions}/components/mail/errors.md +0 -0
  157. /package/wiki/{references → extensions}/components/mail/usage.md +0 -0
  158. /package/wiki/{references → extensions}/components/template/api-page.md +0 -0
  159. /package/wiki/{references → extensions}/components/template/errors-page.md +0 -0
  160. /package/wiki/{references → extensions}/components/template/usage-page.md +0 -0
  161. /package/wiki/{references → extensions}/helpers/cron/index.md +0 -0
  162. /package/wiki/{references → extensions}/helpers/inversion/index.md +0 -0
  163. /package/wiki/{references → extensions}/helpers/network/api.md +0 -0
  164. /package/wiki/{references → extensions}/helpers/network/index.md +0 -0
  165. /package/wiki/{references → extensions}/helpers/redis/index.md +0 -0
  166. /package/wiki/{references → extensions}/helpers/socket-io/api.md +0 -0
  167. /package/wiki/{references → extensions}/helpers/socket-io/index.md +0 -0
  168. /package/wiki/{references → extensions}/helpers/template/single-page.md +0 -0
  169. /package/wiki/{references → extensions}/helpers/uid/index.md +0 -0
  170. /package/wiki/{references → extensions}/helpers/websocket/api.md +0 -0
  171. /package/wiki/{references → extensions}/helpers/worker-thread/index.md +0 -0
  172. /package/wiki/{references → extensions}/src-details/mcp-server.md +0 -0
@@ -6,15 +6,17 @@ Models define your data structure using Drizzle ORM schemas. A model is a single
6
6
 
7
7
  ```typescript
8
8
  // src/models/entities/user.model.ts
9
- import { BaseEntity, extraUserColumns, generateIdColumnDefs, model } from '@venizia/ignis';
10
- import { pgTable } from 'drizzle-orm/pg-core';
9
+ import { BaseEntity, generateIdColumnDefs, generateTzColumnDefs, model } from '@venizia/ignis';
10
+ import { pgTable, text } from 'drizzle-orm/pg-core';
11
11
 
12
12
  @model({ type: 'entity' })
13
13
  export class User extends BaseEntity<typeof User.schema> {
14
14
  // Define schema as static property
15
15
  static override schema = pgTable('User', {
16
16
  ...generateIdColumnDefs({ id: { dataType: 'string' } }),
17
- ...extraUserColumns({ idType: 'string' }),
17
+ ...generateTzColumnDefs(),
18
+ name: text('name').notNull(),
19
+ email: text('email').notNull(),
18
20
  });
19
21
 
20
22
  // Relations (empty array if none)
@@ -126,7 +128,8 @@ static override schema = pgTable('User', {
126
128
  ```typescript
127
129
  static override schema = pgTable('User', {
128
130
  ...generateIdColumnDefs({ id: { dataType: 'string' } }), // id (text with UUID default)
129
- ...extraUserColumns({ idType: 'string' }), // status, audit fields, timestamps
131
+ ...generateTzColumnDefs(), // createdAt, modifiedAt
132
+ ...generateUserAuditColumnDefs(), // createdBy, modifiedBy
130
133
  // ... your fields
131
134
  });
132
135
  ```
@@ -139,7 +142,6 @@ static override schema = pgTable('User', {
139
142
  | `generateTzColumnDefs()` | `createdAt`, `modifiedAt` | Track timestamps |
140
143
  | `generateUserAuditColumnDefs()` | `createdBy`, `modifiedBy` | Track who created/updated |
141
144
  | `generateDataTypeColumnDefs()` | `dataType`, `tValue`, `nValue`, etc. | Configuration tables |
142
- | `extraUserColumns()` | Combines audit + status + type | Full-featured entities |
143
145
 
144
146
  :::note User Audit Options
145
147
  The `generateUserAuditColumnDefs` enricher supports an `allowAnonymous` option (default: `true`). Set to `false` to require authenticated user context and throw errors for anonymous operations:
@@ -208,6 +210,35 @@ const [fullUser] = await connector
208
210
  For complete hidden properties documentation, see the [Models Reference](../../../references/base/models.md#hidden-properties).
209
211
  :::
210
212
 
213
+ ## Default Filter
214
+
215
+ Apply automatic filters to all repository queries. This is commonly used for soft-delete patterns:
216
+
217
+ ```typescript
218
+ @model({
219
+ type: 'entity',
220
+ settings: {
221
+ defaultFilter: { where: { isDeleted: false } },
222
+ hiddenProperties: ['deletedAt'],
223
+ },
224
+ })
225
+ export class Article extends BaseEntity<typeof Article.schema> {
226
+ // ...
227
+ }
228
+ ```
229
+
230
+ The default filter is applied automatically to all read operations. Bypass it with `shouldSkipDefaultFilter: true` in the options:
231
+
232
+ ```typescript
233
+ // Normal query - auto-filters out soft-deleted records
234
+ const articles = await articleRepo.find({});
235
+
236
+ // Include deleted records
237
+ const allArticles = await articleRepo.find({
238
+ options: { shouldSkipDefaultFilter: true },
239
+ });
240
+ ```
241
+
211
242
  ## Authorization Settings
212
243
 
213
244
  Declare your model's authorization principal directly in `@model` settings. The decorator auto-populates `AUTHORIZATION_SUBJECT` for type-safe references in route configs:
@@ -237,9 +268,22 @@ authorize: {
237
268
  ```
238
269
 
239
270
  :::tip
240
- For full authorization integration details, see the [Authorization Usage Reference](../../../references/components/authorization/usage#model-based-resource-references).
271
+ For full authorization integration details, see the [Authorization Usage Reference](../../../extensions/components/authorization/usage#model-based-resource-references).
241
272
  :::
242
273
 
274
+ ## Model Metadata Types
275
+
276
+ The `@model` decorator accepts the following metadata:
277
+
278
+ | Field | Type | Description |
279
+ | :--- | :--- | :--- |
280
+ | `type` | `'entity' \| 'view'` | Whether this is a table or a database view |
281
+ | `tableName` | `string` | Optional explicit table name |
282
+ | `skipMigrate` | `boolean` | Skip this model during migrations |
283
+ | `settings.hiddenProperties` | `string[]` | Properties excluded from all query results |
284
+ | `settings.defaultFilter` | `TFilter` | Default filter auto-applied to all queries |
285
+ | `settings.authorize.principal` | `string` | Authorization subject name for this model |
286
+
243
287
  ## Model Template
244
288
 
245
289
  ```typescript
@@ -71,13 +71,26 @@ export class UserRepository extends ReadableRepository<typeof User.schema> {
71
71
  > - After the first argument, you can inject any additional dependencies you need
72
72
  > - When `@inject` is at param index 0, auto-injection is skipped
73
73
 
74
- ## Repository Types
74
+ ## Repository Hierarchy
75
+
76
+ ```
77
+ AbstractRepository (base + mixins: FieldsVisibilityMixin + DefaultFilterMixin)
78
+
79
+ ReadableRepository (read-only: find, findOne, findById, count, existsWith)
80
+
81
+ PersistableRepository (+ create, updateById, updateAll)
82
+
83
+ DefaultCRUDRepository (+ deleteById, deleteAll) — recommended default
84
+
85
+ SoftDeletableRepository (overrides delete to set deletedAt timestamp)
86
+ ```
75
87
 
76
88
  | Type | Description |
77
89
  |------|-------------|
78
- | `DefaultCRUDRepository` | Full read/write operations |
79
- | `ReadableRepository` | Read-only operations |
80
- | `PersistableRepository` | Write operations only |
90
+ | `ReadableRepository` | Read-only operations. Write operations throw errors. |
91
+ | `PersistableRepository` | Read + write operations (create, update). Extends ReadableRepository. |
92
+ | `DefaultCRUDRepository` | Full CRUD including delete. Extends PersistableRepository. **Recommended for most use cases.** |
93
+ | `SoftDeletableRepository` | Extends DefaultCRUDRepository. Overrides delete to set `deletedAt` timestamp instead of physically removing records. |
81
94
 
82
95
  ## Querying Data
83
96
 
@@ -134,6 +147,40 @@ await repo.updateById({
134
147
  await repo.deleteById({ id: 'uuid-here' });
135
148
  ```
136
149
 
150
+ ## Extra Options
151
+
152
+ All repository operations accept an `options` parameter with these fields:
153
+
154
+ | Option | Type | Description |
155
+ | :--- | :--- | :--- |
156
+ | `transaction` | `ITransaction` | Transaction context for atomic operations |
157
+ | `shouldReturn` | `boolean` | Whether to return created/updated data (default: `true`) |
158
+ | `shouldQueryRange` | `boolean` | Return `{ data, range: { total, skip, limit } }` for pagination |
159
+ | `shouldSkipDefaultFilter` | `boolean` | Bypass the model's default filter (e.g., soft delete) |
160
+
161
+ ```typescript
162
+ // Create without returning data (faster)
163
+ await repo.create({
164
+ data: { code: 'SETTING', group: 'SYSTEM' },
165
+ options: { shouldReturn: false },
166
+ });
167
+
168
+ // Bulk create multiple records
169
+ await repo.createAll({
170
+ data: [
171
+ { code: 'SETTING_A', group: 'SYSTEM' },
172
+ { code: 'SETTING_B', group: 'SYSTEM' },
173
+ ],
174
+ });
175
+
176
+ // Query with pagination range
177
+ const result = await repo.find({
178
+ filter: { limit: 20, skip: 0 },
179
+ options: { shouldQueryRange: true }
180
+ });
181
+ // result = { data: [...], range: { total: 150, skip: 0, limit: 20 } }
182
+ ```
183
+
137
184
  ## Querying with Relations
138
185
 
139
186
  Use `include` to fetch related data. The relation name must match what you defined in `static relations`:
@@ -162,6 +209,34 @@ export class Application extends BaseApplication {
162
209
  }
163
210
  ```
164
211
 
212
+ ## SoftDeletableRepository
213
+
214
+ For soft-delete patterns, use `SoftDeletableRepository` which overrides delete operations to set a `deletedAt` timestamp instead of physically removing records:
215
+
216
+ ```typescript
217
+ import { SoftDeletableRepository, repository, model, BaseEntity } from '@venizia/ignis';
218
+ import { pgTable, timestamp } from 'drizzle-orm/pg-core';
219
+
220
+ @model({
221
+ type: 'entity',
222
+ settings: {
223
+ hiddenProperties: ['deletedAt'],
224
+ defaultFilter: { where: { deletedAt: null } },
225
+ },
226
+ })
227
+ export class Category extends BaseEntity<typeof Category.schema> {
228
+ static override schema = pgTable('Category', {
229
+ ...generateIdColumnDefs({ id: { dataType: 'string' } }),
230
+ ...generateTzColumnDefs(),
231
+ deletedAt: timestamp('deleted_at', { withTimezone: true }),
232
+ name: text('name').notNull(),
233
+ });
234
+ }
235
+
236
+ @repository({ dataSource: PostgresDataSource, model: Category })
237
+ export class CategoryRepository extends SoftDeletableRepository<typeof Category.schema> {}
238
+ ```
239
+
165
240
  ## Repository Template
166
241
 
167
242
  ```typescript
@@ -177,7 +252,7 @@ export class MyModelRepository extends DefaultCRUDRepository<typeof MyModel.sche
177
252
 
178
253
  ### Performance: Core API Optimization
179
254
 
180
- Ignis automatically optimizes "flat" queries (no relations, no field selection) by using Drizzle's Core API. This provides **~15-20% faster** queries for simple reads.
255
+ Ignis automatically optimizes "flat" queries (no relations, no field selection) by using Drizzle's Core API. This provides **~15-20% faster** queries for simple reads. The `canUseCoreAPI()` method on `ReadableRepository` determines when this optimization applies.
181
256
 
182
257
  ### Modular Persistence with Components
183
258
 
@@ -186,9 +261,9 @@ Bundle related persistence resources into Components for better organization:
186
261
  ```typescript
187
262
  export class UserManagementComponent extends BaseComponent {
188
263
  override binding() {
189
- this.application.dataSource(PostgresDataSource);
190
- this.application.repository(UserRepository);
191
- this.application.repository(ProfileRepository);
264
+ this._application.dataSource(PostgresDataSource);
265
+ this._application.repository(UserRepository);
266
+ this._application.repository(ProfileRepository);
192
267
  }
193
268
  }
194
269
  ```
@@ -4,10 +4,10 @@ Ignis supports explicit transaction objects that can be passed across multiple s
4
4
 
5
5
  ## Using Transactions
6
6
 
7
- To use transactions, start one from a repository or datasource, and then pass it to subsequent operations via the `options` parameter.
7
+ To use transactions, start one from a datasource (via the repository's `beginTransaction` method), and then pass it to subsequent operations via the `options` parameter.
8
8
 
9
9
  ```typescript
10
- // 1. Start a transaction
10
+ // 1. Start a transaction from the datasource (accessed through a repository)
11
11
  const tx = await userRepo.beginTransaction({
12
12
  isolationLevel: 'SERIALIZABLE' // Optional, defaults to 'READ COMMITTED'
13
13
  });
@@ -38,6 +38,20 @@ try {
38
38
  }
39
39
  ```
40
40
 
41
+ ## Transaction Object
42
+
43
+ The transaction object returned by `beginTransaction()` has the following properties:
44
+
45
+ | Property/Method | Type | Description |
46
+ | :--- | :--- | :--- |
47
+ | `connector` | `TNodePostgresConnector` | A Drizzle connector bound to the transaction's database client |
48
+ | `isolationLevel` | `TIsolationLevel` | The isolation level of this transaction |
49
+ | `isActive` | `boolean` | Whether the transaction is still active (not yet committed/rolled back) |
50
+ | `commit()` | `Promise<void>` | Commit the transaction and release the connection |
51
+ | `rollback()` | `Promise<void>` | Rollback the transaction and release the connection |
52
+
53
+ Calling `commit()` or `rollback()` on an already-ended transaction throws an error.
54
+
41
55
  ## Isolation Levels
42
56
 
43
57
  Ignis supports standard PostgreSQL isolation levels:
@@ -48,10 +62,13 @@ Ignis supports standard PostgreSQL isolation levels:
48
62
  | `REPEATABLE READ` | Queries see a snapshot as of the start of the transaction. | Reports, consistent reads across multiple queries. |
49
63
  | `SERIALIZABLE` | Strictest level. Emulates serial execution. | Financial transactions, critical data integrity. |
50
64
 
65
+ > [!NOTE]
66
+ > Ignis only supports these three levels. `READ UNCOMMITTED` is **not** accepted — PostgreSQL treats it as `READ COMMITTED` anyway, so Ignis omits it to avoid confusion.
67
+
51
68
  ## Best Practices
52
69
 
53
- 1. **Always use `try...catch...finally`**: Ensure `rollback()` is called on error to release the connection.
54
- 2. **Keep it short**: Long-running transactions hold database locks and connections.
70
+ 1. **Always use `try...catch`**: Ensure `rollback()` is called on error to release the connection back to the pool.
71
+ 2. **Keep it short**: Long-running transactions hold database connections from the pool and can cause connection exhaustion.
55
72
  3. **Pass explicit options**: When calling other services inside a transaction, ensure they accept and use the `transaction` option.
56
73
 
57
74
  ```typescript
@@ -109,19 +126,19 @@ export class OrderService extends BaseService {
109
126
 
110
127
  ```typescript
111
128
  @controller({ path: '/orders' })
112
- export class OrderController extends BaseController {
129
+ export class OrderController extends BaseRestController {
113
130
  constructor(
114
131
  @inject({ key: 'repositories.OrderRepository' })
115
132
  private _orderRepository: OrderRepository,
116
133
  @inject({ key: 'services.OrderService' })
117
134
  private _orderService: OrderService,
118
135
  ) {
119
- super({ scope: OrderController.name, path: '/orders' });
136
+ super({ scope: OrderController.name });
120
137
  }
121
138
 
122
139
  @post({ configs: OrderRoutes.CREATE })
123
140
  async createOrder(c: TRouteContext) {
124
- const body = c.req.valid<{ order: any; items: any[] }>('json'); // Use explicit types for better safety
141
+ const body = c.req.valid<{ order: any; items: any[] }>('json');
125
142
 
126
143
  const tx = await this._orderRepository.beginTransaction({
127
144
  isolationLevel: 'SERIALIZABLE',
@@ -144,6 +161,20 @@ export class OrderController extends BaseController {
144
161
  }
145
162
  ```
146
163
 
164
+ ## How Transactions Work Internally
165
+
166
+ When you pass a `transaction` option to a repository method, the repository uses the transaction's `connector` (a Drizzle instance bound to the transaction's `PoolClient`) instead of the default datasource connector. This ensures all operations within the transaction use the same database connection and see a consistent view of the data.
167
+
168
+ ```typescript
169
+ // Inside AbstractRepository (simplified)
170
+ protected resolveConnector(opts?: { transaction?: ITransaction }) {
171
+ if (opts?.transaction) {
172
+ return opts.transaction.connector;
173
+ }
174
+ return this.dataSource.connector;
175
+ }
176
+ ```
177
+
147
178
  > **Deep Dive:** See [Repository Reference](../../../references/base/repositories/) for more transaction options and patterns.
148
179
 
149
180
  ## See Also
@@ -151,7 +182,7 @@ export class OrderController extends BaseController {
151
182
  - **Related Concepts:**
152
183
  - [Repositories](/guides/core-concepts/persistent/repositories) - Provide transaction API
153
184
  - [Services](/guides/core-concepts/services) - Orchestrate transactional operations
154
- - [Controllers](/guides/core-concepts/controllers) - Initiate transactions from HTTP handlers
185
+ - [Controllers](/guides/core-concepts/rest-controllers) - Initiate transactions from HTTP handlers
155
186
  - [DataSources](/guides/core-concepts/persistent/datasources) - Database connections
156
187
 
157
188
  - **References:**
@@ -1,23 +1,22 @@
1
- # Controllers
1
+ # REST Controllers
2
2
 
3
- Controllers handle HTTP requests and return responses - they're your API endpoints.
3
+ REST controllers handle incoming HTTP requests and return JSON responses -- they are your API endpoints. This is the default transport in Ignis and covers the majority of use cases.
4
4
 
5
- > **Deep Dive:** See [Controllers Reference](../../references/base/controllers.md) for advanced patterns.
5
+ > **Deep Dive:** See [REST Controllers Reference](../../references/base/controllers.md) for the complete API.
6
6
 
7
- ## Creating a Controller
7
+ ## Creating a REST Controller
8
8
 
9
- Extend `BaseController` and use decorators to define routes:
9
+ Extend `BaseRestController` and use decorators to define routes:
10
10
 
11
11
  ```typescript
12
- import { BaseController, controller, get, jsonResponse, z } from '@venizia/ignis';
12
+ import { BaseRestController, controller, get, jsonResponse, z, TRouteContext } from '@venizia/ignis';
13
13
  import { HTTP } from '@venizia/ignis-helpers';
14
- import { Context } from 'hono';
15
14
 
16
15
  @controller({ path: '/users' })
17
- export class UserController extends BaseController {
16
+ export class UserController extends BaseRestController {
18
17
  constructor() {
19
- // It's good practice to pass a scope for logging
20
- super({ scope: UserController.name, path: '/users' });
18
+ // scope is used for logging; path can be omitted if @controller provides it
19
+ super({ scope: UserController.name });
21
20
  }
22
21
 
23
22
  @get({
@@ -29,12 +28,13 @@ export class UserController extends BaseController {
29
28
  }),
30
29
  },
31
30
  })
32
- getAllUsers(c: Context) {
31
+ getAllUsers(c: TRouteContext) {
33
32
  return c.json([{ id: '1', name: 'John Doe' }], HTTP.ResultCodes.RS_2.Ok);
34
33
  }
35
34
  }
36
35
  ```
37
- Notice that the `binding()` method is no longer needed when using decorators.
36
+
37
+ Notice that the `binding()` method is no longer needed when using decorators. The path is resolved from the `@controller` decorator metadata first, then falls back to the constructor `path` option.
38
38
 
39
39
  ## Controller Lifecycle
40
40
 
@@ -42,8 +42,8 @@ Controllers have a simple and predictable lifecycle managed by the application.
42
42
 
43
43
  | Stage | Method | Description |
44
44
  | :--- | :--- | :--- |
45
- | **1. Instantiation** | `constructor(opts)` | The controller is created by the DI container. Dependencies are injected, and you call `super()` to initialize the internal Hono router. |
46
- | **2. Configuration**| `registerControllers` phase | The application automatically discovers and registers all routes defined with decorators (`@get`, `@post`, etc.) on your controller methods. If you have routes defined manually inside `binding()`, that method is also called during this phase. **Note:** If you exclusively use decorators for routing, the `binding()` method can be omitted from your controller. |
45
+ | **1. Instantiation** | `constructor(opts)` | The controller is created by the DI container. Dependencies are injected, and `super()` initializes the internal `OpenAPIHono` router. The `path` is resolved from `@controller` decorator metadata, falling back to the constructor option. |
46
+ | **2. Configuration**| `configure()` | The application calls `configure()` which invokes `binding()` (for manual routes) and `registerRoutesFromRegistry()` (for decorator routes). This method is **idempotent** -- calling it multiple times has no effect after the first call. |
47
47
 
48
48
  ## Defining Routes with Decorators (Recommended)
49
49
 
@@ -71,14 +71,13 @@ The `opts` object contains a `configs` property that defines the route's path, r
71
71
  For optimal organization and type safety, define your route configurations in a constant with `as const`. This allows TypeScript to precisely infer the types for your request data and expected responses within your handler methods.
72
72
 
73
73
  ```typescript
74
- import { BaseController, controller, get, post, jsonContent, jsonResponse, TRouteContext } from '@venizia/ignis';
74
+ import { BaseRestController, controller, get, post, jsonContent, jsonResponse, TRouteContext } from '@venizia/ignis';
75
75
  import { HTTP } from '@venizia/ignis-helpers';
76
76
  import { z } from '@hono/zod-openapi';
77
77
 
78
78
  // Define route configs as const for type inference
79
79
  const TestRoutes = {
80
80
  GET_DATA: {
81
- method: HTTP.Methods.GET,
82
81
  path: '/',
83
82
  responses: jsonResponse({
84
83
  description: 'A simple message',
@@ -86,7 +85,6 @@ const TestRoutes = {
86
85
  }),
87
86
  },
88
87
  CREATE_ITEM: {
89
- method: HTTP.Methods.POST,
90
88
  path: '/',
91
89
  request: {
92
90
  body: jsonContent({
@@ -102,9 +100,9 @@ const TestRoutes = {
102
100
  } as const; // Crucial for strict type inference!
103
101
 
104
102
  @controller({ path: '/my-items' })
105
- export class MyItemsController extends BaseController {
103
+ export class MyItemsController extends BaseRestController {
106
104
  constructor() {
107
- super({ scope: MyItemsController.name, path: '/my-items' });
105
+ super({ scope: MyItemsController.name });
108
106
  }
109
107
 
110
108
  @get({ configs: TestRoutes.GET_DATA })
@@ -171,7 +169,7 @@ import { HTTP } from '@venizia/ignis-helpers';
171
169
  const GetUsersRoute = {
172
170
  path: '/',
173
171
  method: 'get',
174
- authStrategies: [Authentication.STRATEGY_JWT],
172
+ authenticate: { strategies: [Authentication.STRATEGY_JWT] },
175
173
  responses: jsonResponse({
176
174
  description: 'List of all users',
177
175
  schema: z.array(z.object({ id: z.number(), name: z.string() })),
@@ -197,7 +195,7 @@ import { HTTP } from '@venizia/ignis-helpers';
197
195
  // ... inside the binding() method
198
196
 
199
197
  const GetUserByIdRoute = {
200
- path: '/:id',
198
+ path: '/{id}',
201
199
  method: 'get',
202
200
  responses: jsonResponse({
203
201
  description: 'A single user',
@@ -220,7 +218,7 @@ this.bindRoute({
220
218
  For standard CRUD (Create, Read, Update, Delete) operations, `Ignis` provides a `ControllerFactory` that can generate a full-featured controller for any given entity. This significantly reduces boilerplate code.
221
219
 
222
220
  ```typescript
223
- // src/controllers/configuration.controller.ts (Example from @examples/vert)
221
+ // src/controllers/configuration/configuration.controller.ts (Example from @examples/vert)
224
222
  import { Configuration } from '@/models';
225
223
  import { ConfigurationRepository } from '@/repositories';
226
224
  import {
@@ -238,7 +236,7 @@ const _Controller = ControllerFactory.defineCrudController({
238
236
  controller: {
239
237
  name: 'ConfigurationController',
240
238
  basePath: BASE_PATH,
241
- isStrict: true,
239
+ isStrict: { path: true, requestSchema: true },
242
240
  },
243
241
  entity: () => Configuration, // Provide a resolver for your entity class
244
242
  });
@@ -264,12 +262,12 @@ The `ControllerFactory.defineCrudController` method automatically sets up the fo
264
262
  | :--- | :--- | :--- | :--- |
265
263
  | `count` | `GET` | `/count` | Get the number of records matching a filter. |
266
264
  | `find` | `GET` | `/` | Retrieve all records matching a filter. |
267
- | `findById` | `GET` | `/:id` | Retrieve a single record by its ID. |
265
+ | `findById` | `GET` | `/{id}` | Retrieve a single record by its ID. |
268
266
  | `findOne` | `GET` | `/find-one` | Retrieve a single record matching a filter. |
269
267
  | `create` | `POST` | `/` | Create a new record. |
270
- | `updateById` | `PATCH` | `/:id` | Update a single record by its ID. |
268
+ | `updateById` | `PATCH` | `/{id}` | Update a single record by its ID. |
271
269
  | `updateBy` | `PATCH` | `/` | Update multiple records matching a `where` filter. |
272
- | `deleteById` | `DELETE` | `/:id` | Delete a single record by its ID. |
270
+ | `deleteById` | `DELETE` | `/{id}` | Delete a single record by its ID. |
273
271
  | `deleteBy` | `DELETE` | `/` | Delete multiple records matching a `where` filter. |
274
272
 
275
273
  :::info Customization
@@ -357,7 +355,7 @@ export const MainLayout: FC<PropsWithChildren<MainLayoutProps>> = ({ title, chil
357
355
  </header>
358
356
  <main>{children}</main>
359
357
  <footer>
360
- <p 2025 My App</p>
358
+ <p>&copy; 2025 My App</p>
361
359
  </footer>
362
360
  </body>
363
361
  </html>
@@ -373,7 +371,7 @@ When you define Zod schemas in your route's `request` configuration (whether wit
373
371
 
374
372
  ```typescript
375
373
  import { z } from '@hono/zod-openapi';
376
- import { jsonContent, put } from '@venizia/ignis';
374
+ import { jsonContent, put, TRouteContext } from '@venizia/ignis';
377
375
  import { HTTP } from '@venizia/ignis-helpers';
378
376
 
379
377
  // ... inside a controller class
@@ -382,8 +380,7 @@ const UserSchema = z.object({ name: z.string(), email: z.string().email() });
382
380
 
383
381
  @put({
384
382
  configs: {
385
- path: '/:id',
386
- method: 'put',
383
+ path: '/{id}',
387
384
  request: {
388
385
  params: z.object({ id: z.string() }),
389
386
  query: z.object({ notify: z.string().optional() }),
@@ -392,7 +389,7 @@ const UserSchema = z.object({ name: z.string(), email: z.string().email() });
392
389
  // ... responses
393
390
  },
394
391
  })
395
- updateUser(c: Context) {
392
+ updateUser(c: TRouteContext) {
396
393
  // Access validated data from the request
397
394
  const { id } = c.req.valid('param');
398
395
  const { notify } = c.req.valid('query');
@@ -417,8 +414,7 @@ import { HTTP } from '@venizia/ignis-helpers';
417
414
  // ... inside a controller class
418
415
 
419
416
  const UpdateUserConfig = {
420
- path: '/:id',
421
- method: 'put',
417
+ path: '/{id}',
422
418
  request: {
423
419
  params: z.object({ id: z.string() }),
424
420
  query: z.object({ notify: z.string().optional() }),
@@ -453,11 +449,12 @@ Using `TRouteContext` provides a typed context object. By using `c.req.valid<T>(
453
449
  - [Application](/guides/core-concepts/application/) - Registering controllers
454
450
  - [Services](/guides/core-concepts/services) - Business logic layer called by controllers
455
451
  - [Dependency Injection](/guides/core-concepts/dependency-injection) - Injecting services into controllers
452
+ - [gRPC Controllers](/guides/core-concepts/grpc-controllers) - RPC-based controllers
456
453
 
457
454
  - **References:**
458
- - [BaseController API](/references/base/controllers) - Complete API reference
455
+ - [BaseRestController API](/references/base/controllers) - Complete REST controller API reference
459
456
  - [Middlewares](/references/base/middlewares) - Request interceptors
460
- - [Swagger Component](/references/components/swagger) - Auto-generate API docs
457
+ - [Swagger Component](/extensions/components/swagger) - Auto-generate API docs
461
458
  - [Schema Utilities](/references/utilities/schema) - Request/response helpers
462
459
 
463
460
  - **Tutorials:**
@@ -15,11 +15,10 @@ Services contain the core business logic of your application. They orchestrate t
15
15
 
16
16
  ### Creating a Service
17
17
 
18
- To create a service, you extend the `BaseService` class and inject the repositories or other services it depends on.
18
+ To create a service, extend the `BaseService` class and inject the repositories or other services it depends on.
19
19
 
20
20
  ```typescript
21
21
  import { BaseService, inject } from '@venizia/ignis';
22
- import { getError } from '@venizia/ignis-helpers';
23
22
  import { ConfigurationRepository } from '../repositories';
24
23
  import { UserRepository } from '../repositories';
25
24
  import { LoggingService } from './logging.service'; // Example of another service
@@ -27,9 +26,9 @@ import { TConfiguration } from '../models/entities';
27
26
 
28
27
  export class ConfigurationService extends BaseService {
29
28
  constructor(
30
- @inject({ key: 'repositories.ConfigurationRepository' })
29
+ @inject({ key: 'repositories.ConfigurationRepository' })
31
30
  private configurationRepository: ConfigurationRepository,
32
- @inject({ key: 'repositories.UserRepository' })
31
+ @inject({ key: 'repositories.UserRepository' })
33
32
  private userRepository: UserRepository,
34
33
  @inject({ key: 'services.LoggingService' })
35
34
  private loggingService: LoggingService, // Injecting another service for reuse
@@ -49,6 +48,20 @@ export class ConfigurationService extends BaseService {
49
48
  // ...
50
49
  ```
51
50
 
51
+ ### BaseService API
52
+
53
+ `BaseService` is intentionally minimal. It extends `BaseHelper` to provide scoped logging:
54
+
55
+ ```typescript
56
+ export abstract class BaseService extends BaseHelper implements IService {
57
+ constructor(opts: { scope: string }) {
58
+ super({ scope: opts.scope });
59
+ }
60
+ }
61
+ ```
62
+
63
+ There is no built-in CRUD service -- implement business logic directly in your service methods. This keeps the service layer focused on your domain-specific operations rather than generic data access patterns (which belong in repositories).
64
+
52
65
  ## How Services Fit into the Architecture
53
66
 
54
67
  Services act as the primary layer for business logic, sitting between controllers and repositories. While controllers are the typical entry point, **services can also inject and call other services**. This enables powerful logic reuse and allows you to build complex use cases by composing smaller, specialized services.
@@ -88,14 +101,14 @@ This layered architecture makes your application:
88
101
  ## See Also
89
102
 
90
103
  - **Related Concepts:**
91
- - [Controllers](/guides/core-concepts/controllers) - Call services to handle requests
104
+ - [Controllers](/guides/core-concepts/rest-controllers) - Call services to handle requests
92
105
  - [Repositories](/guides/core-concepts/persistent/repositories) - Data access layer used by services
93
106
  - [Dependency Injection](/guides/core-concepts/dependency-injection) - Injecting dependencies into services
94
107
 
95
108
  - **References:**
96
109
  - [BaseService API](/references/base/services) - Complete API reference
97
110
  - [Providers](/references/base/providers) - Factory pattern for runtime instantiation
98
- - [Logger Helper](/references/helpers/logger/) - Logging in services
111
+ - [Logger Helper](/extensions/helpers/logger/) - Logging in services
99
112
 
100
113
  - **Best Practices:**
101
114
  - [Architectural Patterns](/best-practices/architectural-patterns) - Service layer design