@venizia/ignis-docs 0.0.8-3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/README.md +7 -7
  2. package/{wiki → content}/best-practices/api-usage-examples.md +15 -12
  3. package/{wiki → content}/best-practices/architectural-patterns.md +70 -78
  4. package/{wiki → content}/best-practices/architecture-decisions.md +91 -60
  5. package/{wiki → content}/best-practices/code-style-standards/advanced-patterns.md +56 -44
  6. package/{wiki → content}/best-practices/code-style-standards/constants-configuration.md +11 -11
  7. package/{wiki → content}/best-practices/code-style-standards/control-flow.md +5 -2
  8. package/{wiki → content}/best-practices/code-style-standards/documentation.md +13 -13
  9. package/{wiki → content}/best-practices/code-style-standards/function-patterns.md +9 -10
  10. package/{wiki → content}/best-practices/code-style-standards/index.md +1 -1
  11. package/{wiki → content}/best-practices/code-style-standards/naming-conventions.md +10 -8
  12. package/{wiki → content}/best-practices/code-style-standards/route-definitions.md +30 -12
  13. package/{wiki → content}/best-practices/code-style-standards/tooling.md +8 -5
  14. package/{wiki → content}/best-practices/code-style-standards/type-safety.md +13 -12
  15. package/{wiki → content}/best-practices/common-pitfalls.md +56 -37
  16. package/{wiki → content}/best-practices/contribution-workflow.md +13 -14
  17. package/{wiki → content}/best-practices/data-modeling.md +44 -20
  18. package/{wiki → content}/best-practices/deployment-strategies.md +28 -27
  19. package/{wiki → content}/best-practices/error-handling.md +48 -24
  20. package/{wiki → content}/best-practices/index.md +5 -5
  21. package/{wiki → content}/best-practices/performance-optimization.md +36 -28
  22. package/{wiki → content}/best-practices/security-guidelines.md +52 -23
  23. package/{wiki → content}/best-practices/testing-strategies.md +65 -51
  24. package/{wiki → content}/best-practices/troubleshooting-tips.md +24 -24
  25. package/{wiki/extensions/components/swagger.md → content/extensions/components/api-reference.md} +40 -31
  26. package/{wiki → content}/extensions/components/authentication/api.md +19 -19
  27. package/{wiki → content}/extensions/components/authentication/errors.md +7 -7
  28. package/{wiki → content}/extensions/components/authentication/index.md +10 -8
  29. package/{wiki → content}/extensions/components/authentication/usage.md +101 -6
  30. package/{wiki → content}/extensions/components/authorization/api.md +45 -25
  31. package/{wiki → content}/extensions/components/authorization/errors.md +6 -6
  32. package/{wiki → content}/extensions/components/authorization/index.md +11 -10
  33. package/{wiki → content}/extensions/components/authorization/usage.md +21 -21
  34. package/{wiki → content}/extensions/components/health-check.md +1 -1
  35. package/{wiki → content}/extensions/components/index.md +5 -5
  36. package/{wiki → content}/extensions/components/mail/errors.md +15 -15
  37. package/{wiki → content}/extensions/components/mail/index.md +1 -2
  38. package/{wiki → content}/extensions/components/mail/usage.md +1 -1
  39. package/{wiki → content}/extensions/components/request-tracker.md +1 -1
  40. package/{wiki → content}/extensions/components/socket-io/api.md +9 -9
  41. package/{wiki → content}/extensions/components/socket-io/errors.md +5 -5
  42. package/{wiki → content}/extensions/components/socket-io/index.md +8 -8
  43. package/{wiki → content}/extensions/components/socket-io/usage.md +1 -1
  44. package/{wiki → content}/extensions/components/static-asset/api.md +17 -4
  45. package/{wiki → content}/extensions/components/static-asset/errors.md +4 -4
  46. package/{wiki → content}/extensions/components/static-asset/index.md +26 -28
  47. package/{wiki → content}/extensions/components/static-asset/usage.md +13 -12
  48. package/{wiki → content}/extensions/components/template/index.md +2 -2
  49. package/{wiki → content}/extensions/components/template/setup-page.md +1 -1
  50. package/{wiki → content}/extensions/components/websocket/api.md +3 -3
  51. package/{wiki → content}/extensions/components/websocket/errors.md +5 -5
  52. package/{wiki → content}/extensions/components/websocket/index.md +5 -5
  53. package/{wiki → content}/extensions/components/websocket/usage.md +3 -3
  54. package/{wiki → content}/extensions/helpers/cron/index.md +2 -2
  55. package/{wiki → content}/extensions/helpers/crypto/index.md +1 -1
  56. package/{wiki → content}/extensions/helpers/env/index.md +27 -12
  57. package/content/extensions/helpers/error/index.md +283 -0
  58. package/{wiki → content}/extensions/helpers/index.md +2 -3
  59. package/{wiki → content}/extensions/helpers/inversion/index.md +15 -7
  60. package/{wiki → content}/extensions/helpers/kafka/examples.md +1 -1
  61. package/{wiki → content}/extensions/helpers/logger/index.md +32 -2
  62. package/{wiki → content}/extensions/helpers/network/index.md +6 -0
  63. package/{wiki → content}/extensions/helpers/queue/index.md +14 -17
  64. package/content/extensions/helpers/redis/index.md +713 -0
  65. package/{wiki → content}/extensions/helpers/socket-io/index.md +14 -10
  66. package/{wiki → content}/extensions/helpers/storage/api.md +44 -8
  67. package/{wiki → content}/extensions/helpers/storage/index.md +43 -7
  68. package/{wiki → content}/extensions/helpers/template/index.md +6 -3
  69. package/{wiki → content}/extensions/helpers/types/index.md +11 -8
  70. package/{wiki → content}/extensions/helpers/websocket/api.md +9 -9
  71. package/{wiki → content}/extensions/helpers/websocket/index.md +7 -7
  72. package/{wiki → content}/extensions/helpers/worker-thread/index.md +2 -2
  73. package/{wiki → content}/extensions/index.md +3 -4
  74. package/{wiki → content}/extensions/src-details/mcp-server.md +18 -24
  75. package/{wiki → content}/guides/core-concepts/application/bootstrapping.md +11 -14
  76. package/{wiki → content}/guides/core-concepts/application/index.md +3 -3
  77. package/{wiki → content}/guides/core-concepts/components.md +19 -10
  78. package/{wiki → content}/guides/core-concepts/dependency-injection.md +6 -3
  79. package/{wiki → content}/guides/core-concepts/grpc-controllers.md +6 -5
  80. package/{wiki → content}/guides/core-concepts/persistent/datasources.md +33 -27
  81. package/{wiki → content}/guides/core-concepts/persistent/index.md +16 -5
  82. package/{wiki → content}/guides/core-concepts/persistent/models.md +24 -20
  83. package/content/guides/core-concepts/persistent/postgres-drivers.md +167 -0
  84. package/{wiki → content}/guides/core-concepts/persistent/repositories.md +40 -23
  85. package/content/guides/core-concepts/persistent/search-meilisearch.md +183 -0
  86. package/content/guides/core-concepts/persistent/search-typesense.md +429 -0
  87. package/{wiki → content}/guides/core-concepts/persistent/transactions.md +61 -25
  88. package/{wiki → content}/guides/core-concepts/rest-controllers.md +12 -9
  89. package/content/guides/core-concepts/services.md +389 -0
  90. package/{wiki → content}/guides/get-started/5-minute-quickstart.md +19 -19
  91. package/{wiki → content}/guides/get-started/philosophy.md +36 -36
  92. package/{wiki → content}/guides/get-started/setup.md +3 -3
  93. package/{wiki → content}/guides/index.md +3 -3
  94. package/content/guides/migrations/redis-helpers-migration.md +177 -0
  95. package/{wiki → content}/guides/migrations/scoped-rbac-migration.md +17 -17
  96. package/content/guides/migrations/unified-connectors-migration.md +113 -0
  97. package/{wiki → content}/guides/reference/glossary.md +19 -12
  98. package/{wiki → content}/guides/reference/mcp-docs-server.md +22 -18
  99. package/{wiki → content}/guides/tutorials/building-a-crud-api.md +30 -33
  100. package/{wiki → content}/guides/tutorials/complete-installation.md +17 -17
  101. package/{wiki → content}/guides/tutorials/ecommerce-api.md +158 -119
  102. package/{wiki → content}/guides/tutorials/realtime-chat.md +176 -130
  103. package/content/guides/tutorials/testing.md +264 -0
  104. package/content/index.md +5 -0
  105. package/content/public/apple-touch-icon.png +0 -0
  106. package/content/public/og-image.png +0 -0
  107. package/content/public/site.webmanifest +11 -0
  108. package/{wiki → content}/references/base/application.md +4 -5
  109. package/{wiki → content}/references/base/bootstrapping.md +18 -5
  110. package/{wiki → content}/references/base/components.md +149 -120
  111. package/content/references/base/connectors.md +178 -0
  112. package/{wiki → content}/references/base/controllers.md +41 -30
  113. package/content/references/base/datasources.md +527 -0
  114. package/{wiki → content}/references/base/dependency-injection.md +34 -22
  115. package/{wiki → content}/references/base/filter-system/application-usage.md +17 -14
  116. package/{wiki → content}/references/base/filter-system/array-operators.md +7 -2
  117. package/{wiki → content}/references/base/filter-system/comparison-operators.md +3 -0
  118. package/{wiki → content}/references/base/filter-system/default-filter.md +89 -71
  119. package/{wiki → content}/references/base/filter-system/fields-order-pagination.md +22 -22
  120. package/{wiki → content}/references/base/filter-system/index.md +6 -3
  121. package/{wiki → content}/references/base/filter-system/json-filtering.md +20 -1
  122. package/{wiki → content}/references/base/filter-system/list-operators.md +1 -1
  123. package/{wiki → content}/references/base/filter-system/logical-operators.md +33 -1
  124. package/{wiki → content}/references/base/filter-system/null-operators.md +30 -1
  125. package/{wiki → content}/references/base/filter-system/quick-reference.md +23 -4
  126. package/{wiki → content}/references/base/filter-system/tips.md +5 -5
  127. package/{wiki → content}/references/base/filter-system/use-cases.md +12 -12
  128. package/{wiki → content}/references/base/grpc-controllers.md +13 -13
  129. package/{wiki → content}/references/base/index.md +24 -12
  130. package/{wiki/references/base/middleware.md → content/references/base/middlewares.md} +205 -24
  131. package/{wiki → content}/references/base/models.md +63 -49
  132. package/{wiki → content}/references/base/providers.md +136 -130
  133. package/{wiki → content}/references/base/repositories/advanced.md +59 -58
  134. package/{wiki → content}/references/base/repositories/index.md +115 -91
  135. package/content/references/base/repositories/mixins.md +99 -0
  136. package/{wiki → content}/references/base/repositories/relations.md +54 -64
  137. package/{wiki → content}/references/base/repositories/soft-deletable.md +31 -30
  138. package/content/references/base/services.md +404 -0
  139. package/{wiki → content}/references/configuration/environment-variables.md +46 -30
  140. package/{wiki → content}/references/configuration/index.md +6 -6
  141. package/{wiki → content}/references/index.md +17 -12
  142. package/{wiki → content}/references/quick-reference.md +65 -106
  143. package/content/references/utilities/crypto.md +98 -0
  144. package/{wiki → content}/references/utilities/index.md +3 -3
  145. package/{wiki → content}/references/utilities/jsx.md +6 -4
  146. package/content/references/utilities/module.md +90 -0
  147. package/{wiki → content}/references/utilities/parse.md +4 -14
  148. package/{wiki → content}/references/utilities/promise.md +9 -7
  149. package/{wiki → content}/references/utilities/schema.md +5 -3
  150. package/dist/mcp-server/common/guards.d.ts +8 -0
  151. package/dist/mcp-server/common/guards.d.ts.map +1 -0
  152. package/dist/mcp-server/common/guards.js +14 -0
  153. package/dist/mcp-server/common/guards.js.map +1 -0
  154. package/dist/mcp-server/common/index.d.ts +1 -0
  155. package/dist/mcp-server/common/index.d.ts.map +1 -1
  156. package/dist/mcp-server/common/index.js +1 -0
  157. package/dist/mcp-server/common/index.js.map +1 -1
  158. package/dist/mcp-server/common/paths.d.ts.map +1 -1
  159. package/dist/mcp-server/common/paths.js +2 -2
  160. package/dist/mcp-server/common/paths.js.map +1 -1
  161. package/dist/mcp-server/helpers/docs.helper.d.ts.map +1 -1
  162. package/dist/mcp-server/helpers/docs.helper.js +4 -2
  163. package/dist/mcp-server/helpers/docs.helper.js.map +1 -1
  164. package/dist/mcp-server/helpers/github.helper.js +1 -1
  165. package/dist/mcp-server/index.js +7 -2
  166. package/dist/mcp-server/index.js.map +1 -1
  167. package/dist/mcp-server/tools/base.tool.d.ts +6 -2
  168. package/dist/mcp-server/tools/base.tool.d.ts.map +1 -1
  169. package/dist/mcp-server/tools/base.tool.js.map +1 -1
  170. package/dist/mcp-server/tools/docs/search-documents.tool.d.ts +1 -1
  171. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  172. package/dist/mcp-server/tools/github/search-code.tool.d.ts +1 -1
  173. package/dist/mcp-server/tools/github/search-code.tool.d.ts.map +1 -1
  174. package/dist/mcp-server/tools/github/search-code.tool.js +4 -1
  175. package/dist/mcp-server/tools/github/search-code.tool.js.map +1 -1
  176. package/dist/mcp-server/tools/github/verify-dependencies.tool.d.ts.map +1 -1
  177. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +3 -1
  178. package/dist/mcp-server/tools/github/verify-dependencies.tool.js.map +1 -1
  179. package/package.json +12 -12
  180. package/wiki/extensions/helpers/error/index.md +0 -227
  181. package/wiki/extensions/helpers/redis/index.md +0 -488
  182. package/wiki/extensions/helpers/testing/index.md +0 -510
  183. package/wiki/guides/core-concepts/services.md +0 -119
  184. package/wiki/guides/tutorials/testing.md +0 -722
  185. package/wiki/index.md +0 -183
  186. package/wiki/references/base/datasources.md +0 -454
  187. package/wiki/references/base/middlewares.md +0 -590
  188. package/wiki/references/base/repositories/mixins.md +0 -335
  189. package/wiki/references/base/services.md +0 -201
  190. package/wiki/references/utilities/crypto.md +0 -56
  191. package/wiki/references/utilities/module.md +0 -42
  192. /package/{wiki → content}/extensions/components/mail/api.md +0 -0
  193. /package/{wiki → content}/extensions/components/template/api-page.md +0 -0
  194. /package/{wiki → content}/extensions/components/template/errors-page.md +0 -0
  195. /package/{wiki → content}/extensions/components/template/single-page.md +0 -0
  196. /package/{wiki → content}/extensions/components/template/usage-page.md +0 -0
  197. /package/{wiki → content}/extensions/helpers/kafka/admin.md +0 -0
  198. /package/{wiki → content}/extensions/helpers/kafka/consumer.md +0 -0
  199. /package/{wiki → content}/extensions/helpers/kafka/index.md +0 -0
  200. /package/{wiki → content}/extensions/helpers/kafka/producer.md +0 -0
  201. /package/{wiki → content}/extensions/helpers/kafka/schema-registry.md +0 -0
  202. /package/{wiki → content}/extensions/helpers/network/api.md +0 -0
  203. /package/{wiki → content}/extensions/helpers/socket-io/api.md +0 -0
  204. /package/{wiki → content}/extensions/helpers/template/single-page.md +0 -0
  205. /package/{wiki → content}/extensions/helpers/uid/index.md +0 -0
  206. /package/{wiki → content}/guides/core-concepts/components-guide.md +0 -0
  207. /package/{wiki → content}/public/logo.svg +0 -0
  208. /package/{wiki → content}/references/base/filter-system/pattern-matching.md +0 -0
  209. /package/{wiki → content}/references/base/filter-system/range-operators.md +0 -0
  210. /package/{wiki → content}/references/utilities/date.md +0 -0
  211. /package/{wiki → content}/references/utilities/performance.md +0 -0
  212. /package/{wiki → content}/references/utilities/request.md +0 -0
  213. /package/{wiki → content}/references/utilities/statuses.md +0 -0
@@ -1,19 +1,19 @@
1
1
  ---
2
- title: Middleware Reference
2
+ title: Middlewares Reference
3
3
  description: Technical reference for built-in middlewares in IGNIS
4
4
  difficulty: intermediate
5
- lastUpdated: 2026-03-15
5
+ lastUpdated: 2026-06-14
6
6
  ---
7
7
 
8
- # Middleware Reference
8
+ # Middlewares Reference
9
9
 
10
- IGNIS provides built-in middleware functions and a provider-based middleware class for handling common HTTP concerns: error handling, request logging, 404 responses, and favicon serving. These are registered automatically by `BaseApplication` during startup.
10
+ IGNIS provides built-in middleware functions and a provider-based middleware class for handling common HTTP concerns: error handling, request logging, 404 responses, and favicon serving. These are registered automatically by `BaseApplication` during startup - you do not import or wire them manually.
11
11
 
12
12
  **Files:**
13
- - `packages/core/src/base/middlewares/app-error.middleware.ts`
14
- - `packages/core/src/base/middlewares/not-found.middleware.ts`
15
- - `packages/core/src/base/middlewares/request-spy.middleware.ts`
16
- - `packages/core/src/base/middlewares/emoji-favicon.middleware.ts`
13
+ - `packages/core/src/base/middlewares/app-error/app-error.middleware.ts`
14
+ - `packages/core/src/base/middlewares/not-found/not-found.middleware.ts`
15
+ - `packages/core/src/base/middlewares/request-spy/request-spy.middleware.ts`
16
+ - `packages/core/src/base/middlewares/emoji-favicon/emoji-favicon.middleware.ts`
17
17
 
18
18
  ## Prerequisites
19
19
 
@@ -64,6 +64,8 @@ After `registerDefaultMiddlewares()`, the application calls user-defined `static
64
64
 
65
65
  Global error handler registered via `server.onError()`. Handles ZodError validation errors, PostgreSQL constraint violations, and generic errors.
66
66
 
67
+ **Not exported from `@venizia/ignis`** - registered automatically by `BaseApplication`.
68
+
67
69
  ### Signature
68
70
 
69
71
  ```typescript
@@ -82,11 +84,14 @@ function appErrorHandler(opts: {
82
84
 
83
85
  #### 1. ZodError (Validation Errors)
84
86
 
85
- When `error.name === 'ZodError'`, returns HTTP `422 Unprocessable Entity`:
87
+ When `error.name === 'ZodError'`, returns HTTP `422 Unprocessable Entity`.
88
+
89
+ Top-level `message`/`messageCode` come from the first failing issue - its `params.code` if the schema set one, otherwise its raw Zod code. The full per-field list stays under `details.cause`.
86
90
 
87
91
  ```json
88
92
  {
89
- "message": "ValidationError",
93
+ "message": "Invalid email address",
94
+ "messageCode": "user.email.invalid",
90
95
  "statusCode": 422,
91
96
  "requestId": "abc-123",
92
97
  "details": {
@@ -96,7 +101,7 @@ When `error.name === 'ZodError'`, returns HTTP `422 Unprocessable Entity`:
96
101
  "cause": [
97
102
  {
98
103
  "path": "email",
99
- "message": "Invalid email",
104
+ "message": "Invalid email address",
100
105
  "code": "invalid_string",
101
106
  "expected": "string",
102
107
  "received": "undefined"
@@ -106,22 +111,36 @@ When `error.name === 'ZodError'`, returns HTTP `422 Unprocessable Entity`:
106
111
  }
107
112
  ```
108
113
 
114
+ To emit a stable, domain-specific `messageCode`, attach `params.code` to a custom check:
115
+
116
+ ```typescript
117
+ z.string().refine(isEmail, {
118
+ message: 'Invalid email address',
119
+ params: { code: 'user.email.invalid' }
120
+ });
121
+ // produces "messageCode": "user.email.invalid"
122
+ ```
123
+
124
+ > [!NOTE]
125
+ > When `error.message` cannot be parsed as the expected Zod issue array (a malformed or unrecognized `ZodError`), no issue-derived `messageCode` exists - the response still carries one, resolved to `MessageCode.DEFAULT` (`"core.system_error"`) via `MessageCode.resolve(undefined)`. No error response from this middleware is ever missing `messageCode`.
126
+
109
127
  #### 2. PostgreSQL Constraint Violations
110
128
 
111
- Database errors with recognized SQLSTATE codes return HTTP `400 Bad Request` instead of 500:
129
+ Database errors in SQLSTATE class `22` (data exception), `23` (integrity constraint), and `44` (WITH CHECK OPTION violation) are detected by class and returned as HTTP `400 Bad Request`. A known code uses its specific message; any other in-class code uses `"Invalid database request"` as a fallback.
130
+
131
+ | Class | Codes with a specific message |
132
+ |-------|-------------------------------|
133
+ | `23` Integrity | `23505` unique, `23503` foreign key, `23502` not null, `23514` check, `23P01` exclusion, `23000` integrity, `23001` restrict |
134
+ | `22` Data exception | `22001` string too long, `22003` numeric range, `22004` null not allowed, `22007` datetime format, `22008` datetime overflow, `22009` tz displacement, `22011` substring, `22012` division by zero, `22023` invalid parameter, `22025` invalid escape, `22026` length mismatch, `22030` duplicate JSON key, `22032` invalid JSON, `22P01` floating-point, `22P02` invalid text, `22P03` invalid binary, `22P05` untranslatable char |
135
+ | `44` View check | `44000` WITH CHECK OPTION violation |
112
136
 
113
- | SQLSTATE Code | Error Type |
114
- |--------------|------------|
115
- | `23505` | Unique constraint violation |
116
- | `23503` | Foreign key constraint violation |
117
- | `23502` | Not null constraint violation |
118
- | `23514` | Check constraint violation |
119
- | `23P01` | Exclusion constraint violation |
120
- | `22P02` | Invalid text representation |
121
- | `22003` | Numeric value out of range |
122
- | `22001` | String data too long |
137
+ :::tip Transient conflicts return 409, not 400/500
138
+ Class `40` (`40001` serialization failure, `40P01` deadlock) is transient/retryable and returns **409 Conflict** with `messageCode: "database.conflict"` and a safe "please retry" message - the client can safely retry the same request. Programming/infra classes (`42` syntax, `53` resources, `0A`, `25`, `28`) remain 500.
139
+ :::
123
140
 
124
- The error response includes detail, table, and constraint information when available from the database error's `cause` property.
141
+ :::warning Production sanitizes database internals
142
+ In **production** the message is the base message only - `Detail:`/`Table:`/`Constraint:` are stripped (they echo row values and schema names), and `details.stack`/`details.cause` are omitted. Codes outside class 22/23/44 (e.g. `42703` undefined column) and connection failures return a generic `"Internal Server Error"`, so SQL, schema names, and connection host/port never leak.
143
+ :::
125
144
 
126
145
  #### 3. Generic Errors
127
146
 
@@ -132,6 +151,7 @@ All other errors use the `statusCode` property from the error if present, otherw
132
151
  ```json
133
152
  {
134
153
  "message": "Error message",
154
+ "messageCode": "core.system_error",
135
155
  "statusCode": 500,
136
156
  "requestId": "abc-123",
137
157
  "details": {
@@ -149,6 +169,7 @@ When `rootKey` is provided (e.g., `rootKey: 'error'`), the response is wrapped:
149
169
  {
150
170
  "error": {
151
171
  "message": "Error message",
172
+ "messageCode": "core.system_error",
152
173
  "statusCode": 500,
153
174
  "requestId": "abc-123",
154
175
  "details": { ... }
@@ -158,11 +179,38 @@ When `rootKey` is provided (e.g., `rootKey: 'error'`), the response is wrapped:
158
179
 
159
180
  **Production behavior:** `stack` and `cause` fields are omitted when `NODE_ENV` is `'production'`.
160
181
 
182
+ ### Custom Errors
183
+
184
+ Throw any error with a `statusCode` property and the handler picks it up:
185
+
186
+ ```typescript
187
+ class NotFoundError extends Error {
188
+ statusCode = 404;
189
+
190
+ constructor(message: string) {
191
+ super(message);
192
+ this.name = 'NotFoundError';
193
+ }
194
+ }
195
+
196
+ // In a controller
197
+ @get({ configs: GetUserConfig })
198
+ async getUser(c: TRouteContext) {
199
+ const user = await this.userRepository.findById(id);
200
+ if (!user) {
201
+ throw new NotFoundError(`User ${id} not found`);
202
+ }
203
+ return c.json(user, HTTP.ResultCodes.RS_2.Ok);
204
+ }
205
+ ```
206
+
161
207
 
162
208
  ## notFoundHandler
163
209
 
164
210
  Returns a JSON 404 response when no route matches. Registered via `server.notFound()`.
165
211
 
212
+ **Not exported from `@venizia/ignis`** - registered automatically by `BaseApplication`.
213
+
166
214
  ### Signature
167
215
 
168
216
  ```typescript
@@ -194,6 +242,8 @@ The handler logs the 404 at error level with the request ID, path, and full URL.
194
242
 
195
243
  A provider-based middleware class that logs incoming request details and outgoing response timing. It extends `BaseHelper` and implements `IProvider<MiddlewareHandler>`.
196
244
 
245
+ **Not exported from `@venizia/ignis`** - registered automatically via `RequestTrackerComponent` by `BaseApplication`.
246
+
197
247
  ### Class Definition
198
248
 
199
249
  ```typescript
@@ -256,11 +306,37 @@ Returns `null` if no `Content-Type` header or `Content-Length` is `0`/missing. T
256
306
 
257
307
  The middleware resolves the client IP from the connection info or falls back to `x-real-ip` / `x-forwarded-for` headers. If neither is available, it throws HTTP 400 `'Malformed Connection Info'`.
258
308
 
309
+ ### Accessing the Request ID
310
+
311
+ `RequestSpyMiddleware.REQUEST_ID_KEY` is `'requestId'` - since the middleware class itself is not exported from `@venizia/ignis`, read the context variable by that key:
312
+
313
+ ```typescript
314
+ import { get, jsonResponse, TRouteContext } from '@venizia/ignis';
315
+ import { z } from '@hono/zod-openapi';
316
+ import { HTTP } from '@venizia/ignis-helpers';
317
+
318
+ const ExampleConfig = {
319
+ method: HTTP.Methods.GET,
320
+ path: '/example',
321
+ responses: jsonResponse({
322
+ schema: z.object({ requestId: z.string() }),
323
+ }),
324
+ } as const;
325
+
326
+ @get({ configs: ExampleConfig })
327
+ async example(c: TRouteContext) {
328
+ const requestId = c.get('requestId');
329
+ return c.json({ requestId }, HTTP.ResultCodes.RS_2.Ok);
330
+ }
331
+ ```
332
+
259
333
 
260
334
  ## emojiFavicon
261
335
 
262
336
  A simple middleware that serves an emoji as an SVG favicon on `/favicon.ico`.
263
337
 
338
+ **Not exported from `@venizia/ignis`** - registered automatically by `BaseApplication`.
339
+
264
340
  ### Signature
265
341
 
266
342
  ```typescript
@@ -295,7 +371,7 @@ interface IApplicationConfigs {
295
371
 
296
372
  ## User-Defined Middlewares
297
373
 
298
- The `setupMiddlewares()` abstract method on `AbstractApplication` is called after `initialize()` and before the server starts. Use this hook to register additional Hono middlewares:
374
+ The `setupMiddlewares()` abstract method on `AbstractApplication` is called after `initialize()` and before the server starts. Use this hook to register additional Hono middlewares via `this.getServer()`:
299
375
 
300
376
  ```typescript
301
377
  export class MyApplication extends BaseApplication {
@@ -307,6 +383,9 @@ export class MyApplication extends BaseApplication {
307
383
 
308
384
  // Body limit
309
385
  server.use(bodyLimit({ maxSize: 1024 * 1024 })); // 1MB
386
+
387
+ // Route-specific
388
+ server.use('/api/admin/*', adminAuthMiddleware());
310
389
  }
311
390
  }
312
391
  ```
@@ -336,6 +415,105 @@ interface IBaseMiddlewareOptions {
336
415
  ```
337
416
 
338
417
 
418
+ ## Creating Custom Middleware
419
+
420
+ IGNIS uses Hono's middleware system. Create custom middleware using the `createMiddleware` factory from `hono/factory`.
421
+
422
+ ### Basic Middleware
423
+
424
+ ```typescript
425
+ import { createMiddleware } from 'hono/factory';
426
+ import type { MiddlewareHandler } from 'hono';
427
+
428
+ export const myMiddleware = (): MiddlewareHandler => {
429
+ return createMiddleware(async (context, next) => {
430
+ // Before request handling
431
+ console.log('Before:', context.req.path);
432
+
433
+ await next();
434
+
435
+ // After request handling
436
+ console.log('After:', context.req.path);
437
+ });
438
+ };
439
+ ```
440
+
441
+ ### Middleware with Options
442
+
443
+ ```typescript
444
+ interface MyMiddlewareOptions {
445
+ enabled: boolean;
446
+ prefix?: string;
447
+ }
448
+
449
+ export const myMiddleware = (opts: MyMiddlewareOptions): MiddlewareHandler => {
450
+ const { enabled, prefix = 'LOG' } = opts;
451
+
452
+ return createMiddleware(async (context, next) => {
453
+ if (enabled) {
454
+ console.log(`[${prefix}]`, context.req.path);
455
+ }
456
+ await next();
457
+ });
458
+ };
459
+ ```
460
+
461
+ ### Provider-Based Middleware
462
+
463
+ For middleware requiring dependency injection, implement `IProvider<MiddlewareHandler>`:
464
+
465
+ ```typescript
466
+ import { BaseHelper } from '@venizia/ignis-helpers';
467
+ import { IProvider } from '@venizia/ignis-inversion';
468
+ import { createMiddleware } from 'hono/factory';
469
+ import type { MiddlewareHandler } from 'hono';
470
+
471
+ export class MyMiddleware extends BaseHelper implements IProvider<MiddlewareHandler> {
472
+ constructor() {
473
+ super({ scope: MyMiddleware.name });
474
+ }
475
+
476
+ value(): MiddlewareHandler {
477
+ return createMiddleware(async (context, next) => {
478
+ this.logger.info('Processing request:', context.req.path);
479
+ await next();
480
+ });
481
+ }
482
+ }
483
+ ```
484
+
485
+ Register it with `.toProvider()` (the same pattern `RequestTrackerComponent` uses for `RequestSpyMiddleware`), then use the resolved handler inside `setupMiddlewares()` - `get()` returns the produced `MiddlewareHandler` because the container calls `value()` for provider bindings:
486
+
487
+ ```typescript
488
+ export class MyApplication extends BaseApplication {
489
+ preConfigure() {
490
+ this.bind({ key: 'middlewares.MyMiddleware' })
491
+ .toProvider(MyMiddleware)
492
+ .setScope(BindingScopes.SINGLETON);
493
+ }
494
+
495
+ async setupMiddlewares() {
496
+ const myMiddleware = this.get<MiddlewareHandler>({ key: 'middlewares.MyMiddleware' });
497
+ this.getServer().use(myMiddleware);
498
+ }
499
+ }
500
+ ```
501
+
502
+
503
+ ## Performance Considerations
504
+
505
+ ### Request Spy in Production
506
+
507
+ `RequestSpyMiddleware` logs every request. IGNIS automatically skips body logging in production (`NODE_ENV === 'production'`), but the middleware still runs. For ultra-high-traffic workloads consider sampling strategies or externalizing log aggregation.
508
+
509
+ ### Error Logging Volume
510
+
511
+ Error handlers log every error. For high error rates, consider:
512
+ - Sampling (log 1 in N errors)
513
+ - Error aggregation services (Sentry, Rollbar)
514
+ - Rate-limited logging
515
+
516
+
339
517
  ## See Also
340
518
 
341
519
  - **Related References:**
@@ -345,3 +523,6 @@ interface IBaseMiddlewareOptions {
345
523
 
346
524
  - **Guides:**
347
525
  - [Application Guide](/guides/core-concepts/application/)
526
+
527
+ - **External Resources:**
528
+ - [Hono Middleware Documentation](https://hono.dev/docs/guides/middleware)
@@ -6,24 +6,31 @@ difficulty: intermediate
6
6
 
7
7
  # Deep Dive: Models and Enrichers
8
8
 
9
- Technical reference for model architecture and schema enrichers in Ignis.
9
+ Technical reference for model architecture and schema enrichers in IGNIS.
10
+
11
+ > [!IMPORTANT] Base vs. Connectors
12
+ > The engine-neutral root `AbstractEntity` (`packages/core/src/base/models/base.ts`) has no Drizzle, no `pgTable`, and no `drizzle-zod` - just a `name`, an abstract `getSchema()`, a `getIdType(): TIdSchemaType` method (default `'string'`), and `toObject()`/`toJSON()`. Everything described below - the Drizzle-backed `BaseEntity`, `drizzle-zod` schema generation, and all schema enrichers - belongs to the **PostgreSQL connector**'s `BasePostgresEntity`, not the neutral base. See [Connectors](./connectors) for the full base-vs-connectors architecture.
10
13
 
11
14
  **Files:**
12
- - `packages/core/src/base/models/base.ts`
13
- - `packages/core/src/base/models/enrichers/*.ts`
15
+ - `packages/core/src/base/models/base.ts` (neutral `AbstractEntity`)
16
+ - `packages/core/src/connectors/postgres/models/base.ts` (PostgreSQL `BasePostgresEntity`)
17
+ - `packages/core/src/connectors/postgres/models/enrichers/*.ts`
14
18
 
15
19
  ## Quick Reference
16
20
 
17
21
  | Component | Purpose | Key Features |
18
22
  |-----------|---------|--------------|
19
- | **BaseEntity** | Wraps Drizzle schema | Schema encapsulation, Zod generation, `toObject()`/`toJSON()` |
23
+ | **BasePostgresEntity** (alias: `BaseEntity`) | Wraps Drizzle schema | Schema encapsulation, Zod generation, `toObject()`/`toJSON()` |
20
24
  | **Schema Enrichers** | Add common columns to tables | `generateIdColumnDefs()`, `generateTzColumnDefs()`, etc. |
21
25
 
22
- ## `BaseEntity` Class
26
+ ## `BasePostgresEntity` Class (alias: `BaseEntity`)
27
+
28
+ PostgreSQL connector's entity class, wrapping a Drizzle ORM schema. Extends the neutral `AbstractEntity`.
23
29
 
24
- Fundamental building block wrapping a Drizzle ORM schema.
30
+ **File:** `packages/core/src/connectors/postgres/models/base.ts`
25
31
 
26
- **File:** `packages/core/src/base/models/base.ts`
32
+ > [!TIP] Naming
33
+ > `BasePostgresEntity` is the canonical, engine-carrying name. `BaseEntity` is a compatibility alias re-exporting the same class from `connectors/postgres/models/index.ts` (`export { BasePostgresEntity as BaseEntity } from './base'`) - both resolve to identical runtime behavior. Code samples on this page use `BaseEntity` since it remains the most common import today.
27
34
 
28
35
  ### Purpose
29
36
 
@@ -68,13 +75,13 @@ The `@model` decorator marks a class as a database entity and configures its beh
68
75
  | `settings.hiddenProperties` | `string[]` | Array of property names to exclude from all repository query results |
69
76
  | `settings.defaultFilter` | `TFilter` | Filter automatically applied to all repository queries (see [Default Filter](/references/base/filter-system/default-filter)) |
70
77
  | `settings.defaultLimit` | `number` | Default row limit applied when a query omits `limit`. Must be a positive integer (validated at decoration time). Falls back to the global `DEFAULT_LIMIT` (10). See [Pagination](/references/base/filter-system/fields-order-pagination#default-limit) |
71
- | `settings.authorize` | `IModelAuthorizeSettings` | Authorization settings declares the model's authorization principal (see [Authorization](/extensions/components/authorization/usage#model-based-resource-references)) |
78
+ | `settings.authorize` | `IModelAuthorizeSettings` | Authorization settings - declares the model's authorization principal (see [Authorization](/extensions/components/authorization/usage#model-based-resource-references)) |
72
79
  | `settings.authorize.principal` | `string` | The authorization subject name for this model. Auto-populates `AUTHORIZATION_SUBJECT` static property |
73
80
 
74
81
  #### `@model` Behavior
75
82
 
76
83
  When the `@model` decorator is applied:
77
- 1. If `settings.defaultLimit` is provided, it is validated to be a positive integer otherwise the decorator throws at decoration (boot) time
84
+ 1. If `settings.defaultLimit` is provided, it is validated to be a positive integer - otherwise the decorator throws at decoration (boot) time
78
85
  2. If `settings.authorize.principal` is provided and `AUTHORIZATION_SUBJECT` is not already defined on the class, it auto-populates `AUTHORIZATION_SUBJECT` with the principal value
79
86
  3. The model is registered in the `MetadataRegistry` model registry, keyed by table name (resolved as: `metadata.tableName` > `static TABLE_NAME` > class name)
80
87
  4. The static `relations` property is stored as a resolver (not immediately resolved) to avoid circular dependency issues between models
@@ -126,11 +133,11 @@ export class User extends BaseEntity<typeof User.schema> {
126
133
 
127
134
  ```typescript
128
135
  // Repository query - password/secret NOT included
129
- const user = await userRepo.findById({ id: '123' });
136
+ const user = await userRepository.findById({ id: '123' });
130
137
  // user = { id: '123', email: 'john@example.com' }
131
138
 
132
139
  // Direct connector query - ALL fields included
133
- const connector = userRepo.getConnector();
140
+ const connector = userRepository.getConnector();
134
141
  const [fullUser] = await connector
135
142
  .select()
136
143
  .from(User.schema)
@@ -178,11 +185,11 @@ Use `shouldSkipDefaultFilter: true` to bypass:
178
185
 
179
186
  ```typescript
180
187
  // Normal query - includes default filter
181
- await postRepo.find({ filter: {} });
188
+ await postRepository.find({ filter: {} });
182
189
  // WHERE isDeleted = false LIMIT 100
183
190
 
184
191
  // Admin query - bypass default filter
185
- await postRepo.find({
192
+ await postRepository.find({
186
193
  filter: {},
187
194
  options: { shouldSkipDefaultFilter: true }
188
195
  });
@@ -271,7 +278,7 @@ interface IEntity<Schema extends TTableSchemaWithId = TTableSchemaWithId> {
271
278
  |--------|-------------|
272
279
  | `getSchema({ type })` | Get Zod schema for validation (`'select'`, `'create'`, `'update'`) |
273
280
  | `toObject()` | Convert to plain object (shallow spread of `this`) |
274
- | `toJSON()` | Delegates to `toObject()` returns a plain object (used by `JSON.stringify`) |
281
+ | `toJSON()` | Delegates to `toObject()` - returns a plain object (used by `JSON.stringify`) |
275
282
 
276
283
  ### `getSchema` Method
277
284
 
@@ -307,12 +314,11 @@ The `schemaFactory` is a static lazy singleton created via `drizzle-zod`'s `crea
307
314
  ### Class Definition
308
315
 
309
316
  ```typescript
310
- export class BaseEntity<Schema extends TTableSchemaWithId = TTableSchemaWithId>
311
- extends BaseHelper
317
+ export class BasePostgresEntity<Schema extends TTableSchemaWithId = TTableSchemaWithId>
318
+ extends AbstractEntity
312
319
  implements IEntity<Schema>
313
320
  {
314
- // Instance properties
315
- name: string;
321
+ // Instance property (name, toObject(), toJSON() are inherited from AbstractEntity)
316
322
  schema: Schema;
317
323
 
318
324
  // Static properties - override in subclass
@@ -325,23 +331,27 @@ export class BaseEntity<Schema extends TTableSchemaWithId = TTableSchemaWithId>
325
331
  // Performance optimization: avoids creating new factory per entity
326
332
  private static _schemaFactory?: ReturnType<typeof createSchemaFactory>;
327
333
  protected static get schemaFactory(): ReturnType<typeof createSchemaFactory> {
328
- return (BaseEntity._schemaFactory ??= createSchemaFactory());
334
+ return (BasePostgresEntity._schemaFactory ??= createSchemaFactory());
329
335
  }
330
336
 
331
337
  // Constructor supports both patterns
332
338
  constructor(opts?: { name?: string; schema?: Schema }) {
333
- const ctor = new.target as typeof BaseEntity;
339
+ const ctor = new.target as typeof BasePostgresEntity;
334
340
  // Resolution order: opts.name > static TABLE_NAME > class name
335
341
  const name = opts?.name ?? ctor.TABLE_NAME ?? ctor.name;
336
342
 
337
- super({ scope: name });
343
+ super({ name });
338
344
 
339
- this.name = name;
340
345
  this.schema = opts?.schema || (ctor.schema as Schema);
341
346
  }
342
347
 
348
+ // Maps the pgTable id column's Drizzle dataType to 'number' or 'string'
349
+ override getIdType(): TIdSchemaType {
350
+ return getIdType({ entity: this.schema }) === 'number' ? 'number' : 'string';
351
+ }
352
+
343
353
  getSchema(opts: { type: TSchemaType }) {
344
- const factory = BaseEntity.schemaFactory; // Uses static singleton
354
+ const factory = BasePostgresEntity.schemaFactory; // Uses static singleton
345
355
  switch (opts.type) {
346
356
  case SchemaTypes.CREATE:
347
357
  return factory.createInsertSchema(this.schema);
@@ -355,14 +365,6 @@ export class BaseEntity<Schema extends TTableSchemaWithId = TTableSchemaWithId>
355
365
  });
356
366
  }
357
367
  }
358
-
359
- toObject() {
360
- return { ...this };
361
- }
362
-
363
- toJSON() {
364
- return this.toObject();
365
- }
366
368
  }
367
369
  ```
368
370
 
@@ -441,11 +443,11 @@ From `@venizia/ignis-helpers`, enables lazy resolution to avoid circular depende
441
443
  type TValueOrResolver<T> = T | TResolver<T>; // T or () => T
442
444
  ```
443
445
 
444
- Used for `relations` on `BaseEntity` store a function that returns the relations array, resolved lazily when `DataSource.buildSchema()` is called.
446
+ Used for `relations` on `BaseEntity` - store a function that returns the relations array, resolved lazily when `DataSource.buildSchema()` is called.
445
447
 
446
448
  ## Schema Enrichers
447
449
 
448
- Enrichers are helper functions located in `packages/core/src/base/models/enrichers/` that return an object of Drizzle ORM column definitions. They are designed to be spread into a `pgTable` definition to quickly add common, standardized fields to your models.
450
+ Enrichers are helper functions located in `packages/core/src/connectors/postgres/models/enrichers/` that return an object of Drizzle ORM column definitions. They are designed to be spread into a `pgTable` definition to quickly add common, standardized fields to your models.
449
451
 
450
452
  ### Available Enrichers
451
453
 
@@ -456,7 +458,7 @@ Enrichers are helper functions located in `packages/core/src/base/models/enriche
456
458
  | **`generateUserAuditColumnDefs`** | `enrichUserAudit` | Adds `createdBy` and `modifiedBy` columns to track user audit information. |
457
459
  | **`generatePrincipalColumnDefs`** | `enrichPrincipal` | Adds polymorphic principal columns (`{discriminator}Id` and `{discriminator}Type`). |
458
460
  | **`generateDataTypeColumnDefs`** | `enrichDataTypes` | Adds generic data type columns (`dataType`, `nValue`, `tValue`, `bValue`, `jValue`, `boValue`) for flexible data storage. |
459
- | **`extraUserColumns`** | | Adds common user fields (`realm`, `status`, `type`, `activatedAt`, `lastLoginAt`, `parentId`). Imported from `@venizia/ignis` (part of auth component). |
461
+ | **`extraUserColumns`** | - | Adds common user fields (`realm`, `status`, `type`, `activatedAt`, `lastLoginAt`, `parentId`). Imported from `@venizia/ignis` (part of auth component). |
460
462
 
461
463
  Each `generate*` function returns column definition objects for spreading into `pgTable`. The `enrich*` convenience wrappers accept an existing `TColumnDefinitions` object as the first argument and merge the generated columns into it.
462
464
 
@@ -488,7 +490,7 @@ export const myTable = pgTable('MyTable', {
488
490
 
489
491
  Adds a primary key `id` column with support for string UUID, integer, or big integer types with full TypeScript type inference.
490
492
 
491
- **File:** `packages/core/src/base/models/enrichers/id.enricher.ts`
493
+ **File:** `packages/core/src/connectors/postgres/models/enrichers/id.enricher.ts`
492
494
 
493
495
  #### Signature
494
496
 
@@ -688,7 +690,7 @@ const columns = enrichId(
688
690
 
689
691
  Adds timestamp columns for tracking entity creation, modification, and soft deletion.
690
692
 
691
- **File:** `packages/core/src/base/models/enrichers/tz.enricher.ts`
693
+ **File:** `packages/core/src/connectors/postgres/models/enrichers/tz.enricher.ts`
692
694
 
693
695
  #### Signature
694
696
 
@@ -722,7 +724,7 @@ The `modified` and `deleted` options use a discriminated union pattern:
722
724
  | Column | Type | Constraints | Default | Description |
723
725
  |--------|------|-------------|---------|-------------|
724
726
  | `createdAt` | `timestamp` | `NOT NULL` | `now()` | When the record was created (always included) |
725
- | `modifiedAt` | `timestamp` | `NOT NULL` | `now()`, auto-updates via `$onUpdate(() => new Date())` | When the record was last modified (optional, enabled by default) |
727
+ | `modifiedAt` | `timestamp` | `NOT NULL` | `now()`, auto-updates via `$onUpdate(() => new Date().toISOString())` | When the record was last modified (optional, enabled by default) |
726
728
  | `deletedAt` | `timestamp` | nullable | `null` | When the record was soft-deleted (optional, **disabled by default**) |
727
729
 
728
730
  #### Usage Examples
@@ -831,8 +833,10 @@ await db.update(myTable)
831
833
  The enricher provides **conditional TypeScript type inference** based on the options:
832
834
 
833
835
  ```typescript
836
+ type TIsoTimestampColumn = ReturnType<typeof isoTimestamp>; // custom ISO 8601 timestamp column
837
+
834
838
  type TTzEnricherResult<Opts extends TTzEnricherOptions | undefined = undefined> = {
835
- createdAt: NotNull<HasDefault<PgTimestampBuilderInitial<string>>>;
839
+ createdAt: NotNull<HasDefault<TIsoTimestampColumn>>;
836
840
  } & (/* modifiedAt included unless opts.modified.enable === false */)
837
841
  & (/* deletedAt included only when opts.deleted.enable === true */);
838
842
  ```
@@ -854,7 +858,7 @@ Merges timestamp columns into an existing column definitions object.
854
858
 
855
859
  Adds `createdBy` and `modifiedBy` columns to track which user created or modified a record.
856
860
 
857
- **File:** `packages/core/src/base/models/enrichers/user-audit.enricher.ts`
861
+ **File:** `packages/core/src/connectors/postgres/models/enrichers/user-audit.enricher.ts`
858
862
 
859
863
  #### Signature
860
864
 
@@ -888,8 +892,8 @@ type TUserAuditEnricherOptions = {
888
892
 
889
893
  The enricher uses Hono's `contextStorage` (via `tryGetContext()`) to automatically retrieve the current user ID from the request context at insert/update time:
890
894
 
891
- - **`createdBy`**: Set via `$default()` only populated on record creation
892
- - **`modifiedBy`**: Set via both `$default()` and `$onUpdate()` populated on creation and updated on every modification
895
+ - **`createdBy`**: Set via `$default()` - only populated on record creation
896
+ - **`modifiedBy`**: Set via both `$default()` and `$onUpdate()` - populated on creation and updated on every modification
893
897
 
894
898
  The user ID is read from the `Authentication.AUDIT_USER_ID` key in the Hono context.
895
899
 
@@ -1023,7 +1027,7 @@ Merges user audit columns into an existing column definitions object with proper
1023
1027
 
1024
1028
  Adds polymorphic principal columns for associating a record with different entity types. This is the polymorphic association pattern where a row can belong to different parent types (e.g., a comment can belong to a Post, User, or Product).
1025
1029
 
1026
- **File:** `packages/core/src/base/models/enrichers/principal.enricher.ts`
1030
+ **File:** `packages/core/src/connectors/postgres/models/enrichers/principal.enricher.ts`
1027
1031
 
1028
1032
  #### Signature
1029
1033
 
@@ -1142,7 +1146,7 @@ Merges principal columns into an existing column definitions object.
1142
1146
 
1143
1147
  Adds polymorphic data storage columns for entities that need to store values of different types in a single table. This is useful for key-value stores, settings tables, or any schema where a row's value type is determined at runtime.
1144
1148
 
1145
- **File:** `packages/core/src/base/models/enrichers/data-type.enricher.ts`
1149
+ **File:** `packages/core/src/connectors/postgres/models/enrichers/data-type.enricher.ts`
1146
1150
 
1147
1151
  #### Signature
1148
1152
 
@@ -1215,7 +1219,7 @@ export const settingTable = pgTable('Setting', {
1215
1219
  // Generates columns with SQL defaults:
1216
1220
  // data_type text DEFAULT 'text'
1217
1221
  // t_value text DEFAULT ''
1218
- // nValue, bValue, jValue, boValue no defaults
1222
+ // nValue, bValue, jValue, boValue - no defaults
1219
1223
  ```
1220
1224
 
1221
1225
  **Key-value store pattern:**
@@ -1276,7 +1280,7 @@ Generates a Zod schema for path parameters containing an `id` field, suitable fo
1276
1280
  #### Signature
1277
1281
 
1278
1282
  ```typescript
1279
- idParamsSchema(opts?: { idType: string }): z.ZodObject<{ id: z.ZodNumber | z.ZodString }>
1283
+ idParamsSchema(opts?: { idType: TIdSchemaType }): z.ZodObject<{ id: z.ZodNumber | z.ZodString }>
1280
1284
  ```
1281
1285
 
1282
1286
  | `idType` | Default | Zod Type | Examples |
@@ -1479,13 +1483,23 @@ try {
1479
1483
 
1480
1484
  ### `getIdType`
1481
1485
 
1482
- Utility function to determine the data type of an entity's `id` column at runtime:
1486
+ There are two distinct `getIdType`s in the framework - don't confuse them:
1487
+
1488
+ | | Neutral instance method | PostgreSQL utility function |
1489
+ |---|---|---|
1490
+ | **Location** | `AbstractEntity.getIdType()` (`packages/core/src/base/models/base.ts`) | `getIdType()` (`packages/core/src/connectors/postgres/models/common/types.ts`) |
1491
+ | **Signature** | `getIdType(): TIdSchemaType` | `getIdType<T extends TTableSchemaWithId>(opts: { entity: T }): string` |
1492
+ | **Purpose** | Neutral capability every engine's entity implements - returns `'string'` \| `'number'` at the entity level. Used by `idParamsSchema` to build the right Zod schema for path parameters. | PostgreSQL-specific: inspects a Drizzle table schema's `id` column and returns its `dataType` (e.g., `'number'`, `'string'`), or `'unknown'` if not determinable |
1483
1493
 
1484
1494
  ```typescript
1485
- getIdType<T extends TTableSchemaWithId>(opts: { entity: T }): string
1486
- ```
1495
+ // Neutral - instance method every AbstractEntity subclass exposes (default 'string', BasePostgresEntity overrides based on the column)
1496
+ const entity = new User();
1497
+ entity.getIdType(); // 'string' | 'number'
1487
1498
 
1488
- Returns the `dataType` property of the entity's `id` column (e.g., `'number'`, `'string'`), or `'unknown'` if not determinable.
1499
+ // PostgreSQL connector - standalone utility inspecting a raw Drizzle schema
1500
+ import { getIdType } from '@venizia/ignis/postgres';
1501
+ getIdType({ entity: User.schema }); // 'string' | 'number' | 'unknown'
1502
+ ```
1489
1503
 
1490
1504
  ## See Also
1491
1505