@venizia/ignis-docs 0.0.8 → 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 (178) hide show
  1. package/README.md +7 -7
  2. package/content/best-practices/api-usage-examples.md +15 -12
  3. package/content/best-practices/architectural-patterns.md +70 -78
  4. package/content/best-practices/architecture-decisions.md +91 -60
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +56 -44
  6. package/content/best-practices/code-style-standards/constants-configuration.md +11 -11
  7. package/content/best-practices/code-style-standards/control-flow.md +5 -2
  8. package/content/best-practices/code-style-standards/documentation.md +13 -13
  9. package/content/best-practices/code-style-standards/function-patterns.md +9 -10
  10. package/content/best-practices/code-style-standards/index.md +1 -1
  11. package/content/best-practices/code-style-standards/naming-conventions.md +10 -8
  12. package/content/best-practices/code-style-standards/route-definitions.md +30 -12
  13. package/content/best-practices/code-style-standards/tooling.md +8 -5
  14. package/content/best-practices/code-style-standards/type-safety.md +13 -12
  15. package/content/best-practices/common-pitfalls.md +56 -37
  16. package/content/best-practices/contribution-workflow.md +13 -14
  17. package/content/best-practices/data-modeling.md +44 -20
  18. package/content/best-practices/deployment-strategies.md +28 -27
  19. package/content/best-practices/error-handling.md +48 -24
  20. package/content/best-practices/index.md +5 -5
  21. package/content/best-practices/performance-optimization.md +36 -28
  22. package/content/best-practices/security-guidelines.md +52 -23
  23. package/content/best-practices/testing-strategies.md +65 -51
  24. package/content/best-practices/troubleshooting-tips.md +24 -24
  25. package/content/extensions/components/{swagger.md → api-reference.md} +40 -31
  26. package/content/extensions/components/authentication/api.md +19 -19
  27. package/content/extensions/components/authentication/errors.md +7 -7
  28. package/content/extensions/components/authentication/index.md +10 -8
  29. package/content/extensions/components/authentication/usage.md +101 -6
  30. package/content/extensions/components/authorization/api.md +45 -25
  31. package/content/extensions/components/authorization/errors.md +6 -6
  32. package/content/extensions/components/authorization/index.md +11 -10
  33. package/content/extensions/components/authorization/usage.md +21 -21
  34. package/content/extensions/components/health-check.md +1 -1
  35. package/content/extensions/components/index.md +5 -5
  36. package/content/extensions/components/mail/errors.md +15 -15
  37. package/content/extensions/components/mail/index.md +1 -2
  38. package/content/extensions/components/mail/usage.md +1 -1
  39. package/content/extensions/components/request-tracker.md +1 -1
  40. package/content/extensions/components/socket-io/api.md +9 -9
  41. package/content/extensions/components/socket-io/errors.md +5 -5
  42. package/content/extensions/components/socket-io/index.md +8 -8
  43. package/content/extensions/components/socket-io/usage.md +1 -1
  44. package/content/extensions/components/static-asset/api.md +17 -4
  45. package/content/extensions/components/static-asset/errors.md +4 -4
  46. package/content/extensions/components/static-asset/index.md +26 -28
  47. package/content/extensions/components/static-asset/usage.md +13 -12
  48. package/content/extensions/components/template/index.md +2 -2
  49. package/content/extensions/components/template/setup-page.md +1 -1
  50. package/content/extensions/components/websocket/api.md +3 -3
  51. package/content/extensions/components/websocket/errors.md +5 -5
  52. package/content/extensions/components/websocket/index.md +5 -5
  53. package/content/extensions/components/websocket/usage.md +3 -3
  54. package/content/extensions/helpers/cron/index.md +2 -2
  55. package/content/extensions/helpers/crypto/index.md +1 -1
  56. package/content/extensions/helpers/env/index.md +27 -12
  57. package/content/extensions/helpers/error/index.md +81 -25
  58. package/content/extensions/helpers/index.md +2 -3
  59. package/content/extensions/helpers/inversion/index.md +15 -7
  60. package/content/extensions/helpers/kafka/examples.md +1 -1
  61. package/content/extensions/helpers/logger/index.md +32 -2
  62. package/content/extensions/helpers/network/index.md +6 -0
  63. package/content/extensions/helpers/queue/index.md +14 -17
  64. package/content/extensions/helpers/redis/index.md +548 -323
  65. package/content/extensions/helpers/socket-io/index.md +14 -10
  66. package/content/extensions/helpers/storage/api.md +44 -8
  67. package/content/extensions/helpers/storage/index.md +43 -7
  68. package/content/extensions/helpers/template/index.md +6 -3
  69. package/content/extensions/helpers/types/index.md +11 -8
  70. package/content/extensions/helpers/websocket/api.md +9 -9
  71. package/content/extensions/helpers/websocket/index.md +7 -7
  72. package/content/extensions/helpers/worker-thread/index.md +2 -2
  73. package/content/extensions/index.md +3 -4
  74. package/content/extensions/src-details/mcp-server.md +18 -24
  75. package/content/guides/core-concepts/application/bootstrapping.md +11 -14
  76. package/content/guides/core-concepts/application/index.md +3 -3
  77. package/content/guides/core-concepts/components.md +19 -10
  78. package/content/guides/core-concepts/dependency-injection.md +6 -3
  79. package/content/guides/core-concepts/grpc-controllers.md +6 -5
  80. package/content/guides/core-concepts/persistent/datasources.md +33 -27
  81. package/content/guides/core-concepts/persistent/index.md +16 -5
  82. package/content/guides/core-concepts/persistent/models.md +24 -20
  83. package/content/guides/core-concepts/persistent/postgres-drivers.md +167 -0
  84. package/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/content/guides/core-concepts/persistent/transactions.md +61 -25
  88. package/content/guides/core-concepts/rest-controllers.md +12 -9
  89. package/content/guides/core-concepts/services.md +330 -60
  90. package/content/guides/get-started/5-minute-quickstart.md +15 -15
  91. package/content/guides/get-started/philosophy.md +36 -36
  92. package/content/guides/get-started/setup.md +3 -3
  93. package/content/guides/index.md +3 -3
  94. package/content/guides/migrations/redis-helpers-migration.md +177 -0
  95. package/content/guides/migrations/scoped-rbac-migration.md +17 -17
  96. package/content/guides/migrations/unified-connectors-migration.md +113 -0
  97. package/content/guides/reference/glossary.md +19 -12
  98. package/content/guides/reference/mcp-docs-server.md +22 -18
  99. package/content/guides/tutorials/building-a-crud-api.md +30 -33
  100. package/content/guides/tutorials/complete-installation.md +17 -17
  101. package/content/guides/tutorials/ecommerce-api.md +158 -119
  102. package/content/guides/tutorials/realtime-chat.md +176 -130
  103. package/content/guides/tutorials/testing.md +65 -523
  104. package/content/index.md +2 -180
  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/content/references/base/application.md +4 -5
  109. package/content/references/base/bootstrapping.md +18 -5
  110. package/content/references/base/components.md +149 -120
  111. package/content/references/base/connectors.md +178 -0
  112. package/content/references/base/controllers.md +41 -30
  113. package/content/references/base/datasources.md +154 -81
  114. package/content/references/base/dependency-injection.md +34 -22
  115. package/content/references/base/filter-system/application-usage.md +17 -14
  116. package/content/references/base/filter-system/array-operators.md +7 -2
  117. package/content/references/base/filter-system/comparison-operators.md +3 -0
  118. package/content/references/base/filter-system/default-filter.md +89 -71
  119. package/content/references/base/filter-system/fields-order-pagination.md +22 -22
  120. package/content/references/base/filter-system/index.md +6 -3
  121. package/content/references/base/filter-system/json-filtering.md +20 -1
  122. package/content/references/base/filter-system/list-operators.md +1 -1
  123. package/content/references/base/filter-system/logical-operators.md +33 -1
  124. package/content/references/base/filter-system/null-operators.md +30 -1
  125. package/content/references/base/filter-system/quick-reference.md +23 -4
  126. package/content/references/base/filter-system/tips.md +5 -5
  127. package/content/references/base/filter-system/use-cases.md +12 -12
  128. package/content/references/base/grpc-controllers.md +13 -13
  129. package/content/references/base/index.md +24 -12
  130. package/content/references/base/middlewares.md +265 -327
  131. package/content/references/base/models.md +63 -49
  132. package/content/references/base/providers.md +136 -130
  133. package/content/references/base/repositories/advanced.md +59 -58
  134. package/content/references/base/repositories/index.md +115 -91
  135. package/content/references/base/repositories/mixins.md +55 -291
  136. package/content/references/base/repositories/relations.md +54 -64
  137. package/content/references/base/repositories/soft-deletable.md +31 -30
  138. package/content/references/base/services.md +296 -93
  139. package/content/references/configuration/environment-variables.md +46 -30
  140. package/content/references/configuration/index.md +6 -6
  141. package/content/references/index.md +17 -12
  142. package/content/references/quick-reference.md +65 -106
  143. package/content/references/utilities/crypto.md +65 -23
  144. package/content/references/utilities/index.md +3 -3
  145. package/content/references/utilities/jsx.md +6 -4
  146. package/content/references/utilities/module.md +68 -20
  147. package/content/references/utilities/parse.md +4 -14
  148. package/content/references/utilities/promise.md +9 -7
  149. package/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/helpers/docs.helper.d.ts.map +1 -1
  159. package/dist/mcp-server/helpers/docs.helper.js +4 -2
  160. package/dist/mcp-server/helpers/docs.helper.js.map +1 -1
  161. package/dist/mcp-server/helpers/github.helper.js +1 -1
  162. package/dist/mcp-server/index.js +7 -2
  163. package/dist/mcp-server/index.js.map +1 -1
  164. package/dist/mcp-server/tools/base.tool.d.ts +6 -2
  165. package/dist/mcp-server/tools/base.tool.d.ts.map +1 -1
  166. package/dist/mcp-server/tools/base.tool.js.map +1 -1
  167. package/dist/mcp-server/tools/docs/search-documents.tool.d.ts +1 -1
  168. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  169. package/dist/mcp-server/tools/github/search-code.tool.d.ts +1 -1
  170. package/dist/mcp-server/tools/github/search-code.tool.d.ts.map +1 -1
  171. package/dist/mcp-server/tools/github/search-code.tool.js +4 -1
  172. package/dist/mcp-server/tools/github/search-code.tool.js.map +1 -1
  173. package/dist/mcp-server/tools/github/verify-dependencies.tool.d.ts.map +1 -1
  174. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +3 -1
  175. package/dist/mcp-server/tools/github/verify-dependencies.tool.js.map +1 -1
  176. package/package.json +9 -9
  177. package/content/extensions/helpers/testing/index.md +0 -510
  178. package/content/references/base/middleware.md +0 -347
@@ -1,111 +1,110 @@
1
1
  ---
2
2
  title: Middlewares Reference
3
- description: Technical reference for IGNIS built-in middlewares
3
+ description: Technical reference for built-in middlewares in IGNIS
4
4
  difficulty: intermediate
5
- lastUpdated: 2026-01-03
5
+ lastUpdated: 2026-06-14
6
6
  ---
7
7
 
8
8
  # Middlewares Reference
9
9
 
10
- IGNIS provides a collection of built-in middlewares for common application needs including error handling, request logging, and favicon serving.
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/*.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`
14
17
 
15
18
  ## Prerequisites
16
19
 
17
- - [Hono Middleware basics](https://hono.dev/docs/guides/middleware)
18
- - [IGNIS Application basics](./application.md)
19
- - Basic understanding of HTTP request/response lifecycle
20
+ Before reading this document, you should understand:
21
+ - [Hono middleware](https://hono.dev/docs/guides/middleware) basics
22
+ - [Application lifecycle](./application.md)
23
+ - [Providers](./providers.md) - `RequestSpyMiddleware` implements `IProvider`
20
24
 
21
25
  ## Quick Reference
22
26
 
23
- | Middleware | Purpose | Key Options |
24
- |------------|---------|-------------|
25
- | `appErrorHandler` | Catches and formats application errors | `logger` |
26
- | `notFoundHandler` | Handles 404 Not Found responses | `logger` |
27
- | `RequestSpyMiddleware` | Logs request lifecycle, timing, and parses request body | None |
28
- | `emojiFavicon` | Serves an emoji as favicon | `icon` |
27
+ | Middleware | Type | Purpose |
28
+ |-----------|------|---------|
29
+ | `appErrorHandler` | `ErrorHandler` | Global error handler (Zod, DB constraints, generic) |
30
+ | `notFoundHandler` | `NotFoundHandler` | JSON 404 response for unknown routes |
31
+ | `RequestSpyMiddleware` | `IProvider<MiddlewareHandler>` | Request/response logging with timing |
32
+ | `emojiFavicon` | `MiddlewareHandler` | Serves an emoji as SVG favicon |
29
33
 
30
- ## Table of Contents
34
+ ## Default Registration Order
31
35
 
32
- - [Error Handler (`appErrorHandler`)](#error-handler-apporerrorhandler)
33
- - [Not Found Handler (`notFoundHandler`)](#not-found-handler-notfoundhandler)
34
- - [Request Spy (`RequestSpyMiddleware`)](#request-spy-requestspymiddleware)
35
- - [Emoji Favicon](#emoji-favicon)
36
- - [Creating Custom Middleware](#creating-custom-middleware)
37
- - [Middleware Order & Priority](#middleware-order--priority)
38
- - [See Also](#see-also)
36
+ `BaseApplication.registerDefaultMiddlewares()` registers middleware in this order during `initialize()`:
39
37
 
40
- ## Built-in Middlewares
38
+ ```typescript
39
+ protected async registerDefaultMiddlewares() {
40
+ const server = this.getServer();
41
41
 
42
- ### Error Handler (`appErrorHandler`)
42
+ // 1. Global error handler
43
+ server.onError(appErrorHandler({ logger, rootKey }));
43
44
 
44
- The error handler middleware catches all unhandled errors in your application and formats them into consistent JSON responses.
45
+ // 2. Async context storage (if enabled)
46
+ if (this.configs.asyncContext?.enable) {
47
+ server.use(contextStorage());
48
+ }
45
49
 
46
- **File:** `packages/core/src/base/middlewares/app-error/app-error.middleware.ts`
50
+ // 3. Not-found handler
51
+ server.notFound(notFoundHandler({ logger }));
47
52
 
48
- #### Features
53
+ // 4. RequestTrackerComponent (requestId + RequestSpyMiddleware)
54
+ this.component(RequestTrackerComponent);
49
55
 
50
- - **Automatic Error Formatting**: Converts all errors to structured JSON responses
51
- - **ZodError Support**: Validation errors surface a schema-driven `messageCode` and `message` (from `params.code`, else the raw Zod code), with the full per-field list under `details.cause`
52
- - **Database Error Handling**: Returns 400 for SQLSTATE class `22` (data exception) and `23` (integrity) errors, with a fallback message; other classes (e.g. `42` programming errors) stay 500
53
- - **Production-Safe**: Hides stack traces, error causes, DB driver internals (`detail`/`table`/`constraint`), and raw system messages in production
54
- - **Request Tracking**: Includes `requestId` for debugging and tracing
55
- - **Status Code Detection**: Automatically extracts `statusCode` from errors
56
+ // 5. Emoji favicon
57
+ server.use(emojiFavicon({ icon: this.configs.favicon ?? '🔥' }));
58
+ }
59
+ ```
56
60
 
57
- #### Usage
61
+ After `registerDefaultMiddlewares()`, the application calls user-defined `staticConfigure()`, `preConfigure()`, and so on. The user's `setupMiddlewares()` hook runs after `initialize()` but before the server starts.
58
62
 
59
- ```typescript
60
- import { appErrorHandler } from '@venizia/ignis';
63
+ ## appErrorHandler
61
64
 
62
- const app = new IgnisApplication({
63
- // ...
64
- });
65
+ Global error handler registered via `server.onError()`. Handles ZodError validation errors, PostgreSQL constraint violations, and generic errors.
65
66
 
66
- // Register error handler
67
- app.onError(appErrorHandler({
68
- logger: app.logger
69
- }));
70
- ```
67
+ **Not exported from `@venizia/ignis`** - registered automatically by `BaseApplication`.
71
68
 
72
- #### Error Response Format
69
+ ### Signature
73
70
 
74
- **Standard Error:**
75
- ```json
76
- {
77
- "message": "Something went wrong",
78
- "statusCode": 500,
79
- "requestId": "abc123",
80
- "details": {
81
- "url": "http://localhost:3000/api/users",
82
- "path": "/api/users",
83
- "stack": "Error: Something went wrong\n at ...", // development only
84
- "cause": { ... } // development only
85
- }
86
- }
71
+ ```typescript
72
+ function appErrorHandler(opts: {
73
+ logger: Logger;
74
+ rootKey?: string;
75
+ }): ErrorHandler
87
76
  ```
88
77
 
89
- **Validation Error (ZodError):**
78
+ | Parameter | Type | Description |
79
+ |-----------|------|-------------|
80
+ | `logger` | `Logger` | Logger instance for error logging |
81
+ | `rootKey` | `string \| undefined` | Optional root key to wrap the error response object |
82
+
83
+ ### Error Handling Logic
84
+
85
+ #### 1. ZodError (Validation Errors)
86
+
87
+ When `error.name === 'ZodError'`, returns HTTP `422 Unprocessable Entity`.
90
88
 
91
- Top-level `message`/`messageCode` come from the first failing issue its `params.code` if the schema set one (see below), otherwise its raw Zod code. The full per-field list stays under `details.cause`. If `error.rootKey` is configured, the whole body is wrapped under that key.
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`.
92
90
 
93
91
  ```json
94
92
  {
95
93
  "message": "Invalid email address",
96
- "messageCode": "invalid_type",
94
+ "messageCode": "user.email.invalid",
97
95
  "statusCode": 422,
98
- "requestId": "abc123",
96
+ "requestId": "abc-123",
99
97
  "details": {
100
- "url": "http://localhost:3000/api/users",
101
- "path": "/api/users",
102
- "stack": "...", // development only
98
+ "url": "http://localhost:3000/users",
99
+ "path": "/users",
100
+ "stack": "...(non-production only)",
103
101
  "cause": [
104
102
  {
105
103
  "path": "email",
106
104
  "message": "Invalid email address",
107
- "code": "invalid_type",
108
- "expected": "string"
105
+ "code": "invalid_string",
106
+ "expected": "string",
107
+ "received": "undefined"
109
108
  }
110
109
  ]
111
110
  }
@@ -115,55 +114,76 @@ Top-level `message`/`messageCode` come from the first failing issue — its `par
115
114
  To emit a stable, domain-specific `messageCode`, attach `params.code` to a custom check:
116
115
 
117
116
  ```typescript
118
- z.string().refine(isEmail, { message: 'Invalid email address', params: { code: 'user.email.invalid' } });
119
- // "messageCode": "user.email.invalid"
117
+ z.string().refine(isEmail, {
118
+ message: 'Invalid email address',
119
+ params: { code: 'user.email.invalid' }
120
+ });
121
+ // produces "messageCode": "user.email.invalid"
120
122
  ```
121
123
 
122
- **Database Constraint Error:**
123
-
124
- Database errors in SQLSTATE class `22` (data exception) and `23` (integrity constraint) are detected by **class** and returned as 400 Bad Request. A known code uses its specific message; any other in-class code uses `DATABASE_CLIENT_ERROR_FALLBACK_MESSAGE` (`"Invalid database request"`).
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`.
125
126
 
126
- ```json
127
- // non-production — full driver context for debugging
128
- {
129
- "message": "Unique constraint violation\nDetail: Key (email)=(test@example.com) already exists.\nTable: User\nConstraint: UQ_User_email",
130
- "statusCode": 400,
131
- "requestId": "abc123",
132
- "details": { "url": "...", "path": "/api/users", "stack": "...", "cause": { } }
133
- }
134
- ```
135
-
136
- :::warning Production sanitizes database internals
137
- 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 (e.g. `42703` undefined column) and connection failures return a generic `"Internal Server Error"`, so SQL, schema names, and connection host/port never leak.
138
- :::
127
+ #### 2. PostgreSQL Constraint Violations
139
128
 
140
- **Database client error classes** — codes in SQLSTATE class `22` (data exception), `23` (integrity constraint), and `44` (WITH CHECK OPTION) map to HTTP 400. Common codes get a specific message; any other in-class code uses the fallback (`"Invalid database request"`).
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.
141
130
 
142
131
  | Class | Codes with a specific message |
143
132
  |-------|-------------------------------|
144
- | `23` Integrity | `23505` unique · `23503` foreign key · `23502` not null · `23514` check · `23P01` exclusion · `23000` integrity · `23001` restrict |
145
- | `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 |
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 |
146
135
  | `44` View check | `44000` WITH CHECK OPTION violation |
147
136
 
148
137
  :::tip Transient conflicts return 409, not 400/500
149
- 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.
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.
150
139
  :::
151
140
 
152
- #### API Reference
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
+ :::
153
144
 
154
- ##### `appErrorHandler(options)`
145
+ #### 3. Generic Errors
155
146
 
156
- **Parameters:**
157
- | Option | Type | Required | Description |
158
- |--------|------|----------|-------------|
159
- | `logger` | `ApplicationLogger` | Yes | Logger instance for error logging |
147
+ All other errors use the `statusCode` property from the error if present, otherwise default to HTTP `500 Internal Server Error`.
160
148
 
161
- **Returns:** `ErrorHandler` - Hono error handler function
149
+ ### Response Format
162
150
 
163
- #### Common Patterns
151
+ ```json
152
+ {
153
+ "message": "Error message",
154
+ "messageCode": "core.system_error",
155
+ "statusCode": 500,
156
+ "requestId": "abc-123",
157
+ "details": {
158
+ "url": "http://localhost:3000/users",
159
+ "path": "/users",
160
+ "stack": "...(non-production only)",
161
+ "cause": "...(non-production only)"
162
+ }
163
+ }
164
+ ```
165
+
166
+ When `rootKey` is provided (e.g., `rootKey: 'error'`), the response is wrapped:
167
+
168
+ ```json
169
+ {
170
+ "error": {
171
+ "message": "Error message",
172
+ "messageCode": "core.system_error",
173
+ "statusCode": 500,
174
+ "requestId": "abc-123",
175
+ "details": { ... }
176
+ }
177
+ }
178
+ ```
179
+
180
+ **Production behavior:** `stack` and `cause` fields are omitted when `NODE_ENV` is `'production'`.
181
+
182
+ ### Custom Errors
183
+
184
+ Throw any error with a `statusCode` property and the handler picks it up:
164
185
 
165
186
  ```typescript
166
- // Custom error with status code
167
187
  class NotFoundError extends Error {
168
188
  statusCode = 404;
169
189
 
@@ -173,21 +193,9 @@ class NotFoundError extends Error {
173
193
  }
174
194
  }
175
195
 
176
- // Throw in controller
177
- const GetUserConfig = {
178
- method: HTTP.Methods.GET,
179
- path: '/users/:id',
180
- request: {
181
- params: z.object({ id: z.string() }),
182
- },
183
- responses: jsonResponse({
184
- schema: z.object({ id: z.string(), name: z.string() }),
185
- }),
186
- } as const;
187
-
196
+ // In a controller
188
197
  @get({ configs: GetUserConfig })
189
198
  async getUser(c: TRouteContext) {
190
- const { id } = c.req.valid<{ id: string }>('param');
191
199
  const user = await this.userRepository.findById(id);
192
200
  if (!user) {
193
201
  throw new NotFoundError(`User ${id} not found`);
@@ -197,113 +205,114 @@ async getUser(c: TRouteContext) {
197
205
  ```
198
206
 
199
207
 
200
- ### Not Found Handler (`notFoundHandler`)
208
+ ## notFoundHandler
201
209
 
202
- Handles requests to routes that don't exist, returning a standardized 404 response.
210
+ Returns a JSON 404 response when no route matches. Registered via `server.notFound()`.
203
211
 
204
- **File:** `packages/core/src/base/middlewares/not-found.middleware.ts`
212
+ **Not exported from `@venizia/ignis`** - registered automatically by `BaseApplication`.
205
213
 
206
- #### Usage
214
+ ### Signature
207
215
 
208
216
  ```typescript
209
- import { notFoundHandler } from '@venizia/ignis';
210
-
211
- const app = new IgnisApplication({
212
- // ...
213
- });
214
-
215
- // Register 404 handler
216
- app.notFound(notFoundHandler({
217
- logger: app.logger
218
- }));
217
+ function notFoundHandler(opts: {
218
+ logger?: Logger;
219
+ }): NotFoundHandler
219
220
  ```
220
221
 
221
- #### Response Format
222
+ | Parameter | Type | Description |
223
+ |-----------|------|-------------|
224
+ | `logger` | `Logger \| undefined` | Logger instance (defaults to `console`) |
225
+
226
+ ### Response Format
222
227
 
223
228
  ```json
224
229
  {
225
230
  "message": "URL NOT FOUND",
226
- "path": "/api/nonexistent",
227
- "url": "http://localhost:3000/api/nonexistent"
231
+ "statusCode": 404,
232
+ "requestId": "abc-123",
233
+ "path": "/unknown",
234
+ "url": "http://localhost:3000/unknown"
228
235
  }
229
236
  ```
230
237
 
231
- **Status Code:** `404 Not Found`
238
+ The handler logs the 404 at error level with the request ID, path, and full URL.
232
239
 
233
- #### API Reference
234
240
 
235
- ##### `notFoundHandler(options)`
241
+ ## RequestSpyMiddleware
236
242
 
237
- **Parameters:**
238
- | Option | Type | Required | Default | Description |
239
- |--------|------|----------|---------|-------------|
240
- | `logger` | `ApplicationLogger` | No | `console` | Logger instance for logging 404s |
243
+ A provider-based middleware class that logs incoming request details and outgoing response timing. It extends `BaseHelper` and implements `IProvider<MiddlewareHandler>`.
241
244
 
242
- **Returns:** `NotFoundHandler` - Hono not found handler function
245
+ **Not exported from `@venizia/ignis`** - registered automatically via `RequestTrackerComponent` by `BaseApplication`.
243
246
 
247
+ ### Class Definition
244
248
 
245
- ### Request Spy (`RequestSpyMiddleware`)
249
+ ```typescript
250
+ export class RequestSpyMiddleware extends BaseHelper implements IProvider<MiddlewareHandler> {
251
+ static readonly REQUEST_ID_KEY = 'requestId';
246
252
 
247
- Logs detailed information about each request including timing, IP address, method, path, query parameters, and request body. Also handles request body parsing for JSON, form data, and text content types.
253
+ constructor() {
254
+ super({ scope: 'SpyMW' });
255
+ }
248
256
 
249
- **File:** `packages/core/src/base/middlewares/request-spy.middleware.ts`
257
+ async parseBody(opts: { req: TContext['req'] }): Promise<unknown>;
258
+ value(): MiddlewareHandler;
259
+ }
260
+ ```
250
261
 
251
- #### Features
262
+ ### How It Is Registered
252
263
 
253
- - Request lifecycle logging (incoming/outgoing)
254
- - Performance timing tracking
255
- - IP address extraction (supports `x-real-ip` and `x-forwarded-for` headers)
256
- - Request ID tracking
257
- - Query and body parameter logging (body only logged in non-production)
258
- - **Request body parsing**: Automatically parses and caches request bodies:
259
- - `application/json` → `req.json()`
260
- - `multipart/form-data`, `application/x-www-form-urlencoded` → `req.parseBody()`
261
- - Other content types (text, html, xml) → `req.text()`
264
+ `RequestSpyMiddleware` is not registered directly. Instead, `BaseApplication.registerDefaultMiddlewares()` registers a `RequestTrackerComponent`, which:
262
265
 
263
- #### Usage
266
+ 1. Adds the `requestId()` middleware from `hono/request-id` to assign a unique ID to every request
267
+ 2. Binds `RequestSpyMiddleware` as a singleton provider in the DI container
268
+ 3. Resolves the middleware via `IProvider.value()` and registers it with `server.use()`
264
269
 
265
- ```typescript
266
- import { RequestSpyMiddleware } from '@venizia/ignis';
270
+ ### Request Logging
267
271
 
268
- const app = new IgnisApplication({
269
- // ...
270
- });
272
+ In **non-production** mode, logs the full request including query and body:
271
273
 
272
- // Create and register spy middleware
273
- const requestSpy = new RequestSpyMiddleware();
274
- app.use(requestSpy.value());
274
+ ```
275
+ [requestId][clientIp][=>] METHOD /path | query: {...} | body: {...}
275
276
  ```
276
277
 
277
- #### Log Output
278
+ In **production** mode, body is excluded:
278
279
 
279
- **Request Start:**
280
280
  ```
281
- [spy][abc123] START | Handling Request | forwardedIp: 192.168.1.1 | path: /api/users | method: GET
281
+ [requestId][clientIp][=>] METHOD /path | query: {...}
282
282
  ```
283
283
 
284
- **Request Complete:**
284
+ ### Response Logging
285
+
286
+ After the handler completes:
287
+
285
288
  ```
286
- [spy][abc123] DONE | Handling Request | forwardedIp: 192.168.1.1 | path: /api/users | method: GET | Took: 45.23 (ms)
289
+ [requestId][clientIp][<=] METHOD /path | Took: 12.34 (ms)
287
290
  ```
288
291
 
289
- #### API Reference
292
+ ### Body Parsing
293
+
294
+ The `parseBody` method parses the request body based on `Content-Type`:
295
+
296
+ | Content-Type | Parse Method |
297
+ |-------------|-------------|
298
+ | `application/json` | `req.json()` |
299
+ | `multipart/form-data` | `req.parseBody()` |
300
+ | `application/x-www-form-urlencoded` | `req.parseBody()` |
301
+ | Other | `req.text()` |
302
+
303
+ Returns `null` if no `Content-Type` header or `Content-Length` is `0`/missing. Throws HTTP 400 `'Malformed Body Payload'` on parse failure.
290
304
 
291
- ##### `RequestSpyMiddleware`
305
+ ### IP Detection
292
306
 
293
- **Class Methods:**
294
- | Method | Returns | Description |
295
- |--------|---------|-------------|
296
- | `value()` | `MiddlewareHandler` | Returns the middleware handler |
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'`.
297
308
 
298
- **Static Properties:**
299
- | Property | Type | Value | Description |
300
- |----------|------|-------|-------------|
301
- | `REQUEST_ID_KEY` | `string` | `'requestId'` | Context key for request ID |
309
+ ### Accessing the Request ID
302
310
 
303
- #### Accessing Request ID
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:
304
312
 
305
313
  ```typescript
306
- import { RequestSpyMiddleware, get, jsonResponse, TRouteContext, z } from '@venizia/ignis';
314
+ import { get, jsonResponse, TRouteContext } from '@venizia/ignis';
315
+ import { z } from '@hono/zod-openapi';
307
316
  import { HTTP } from '@venizia/ignis-helpers';
308
317
 
309
318
  const ExampleConfig = {
@@ -314,75 +323,101 @@ const ExampleConfig = {
314
323
  }),
315
324
  } as const;
316
325
 
317
- // In a controller
318
326
  @get({ configs: ExampleConfig })
319
327
  async example(c: TRouteContext) {
320
- const requestId = c.get(RequestSpyMiddleware.REQUEST_ID_KEY);
321
- console.log('Request ID:', requestId);
328
+ const requestId = c.get('requestId');
322
329
  return c.json({ requestId }, HTTP.ResultCodes.RS_2.Ok);
323
330
  }
324
331
  ```
325
332
 
326
- :::warning Performance Impact
327
- Request spy logs every request detail. Consider disabling or reducing verbosity in production environments with high traffic.
328
- :::
329
-
330
333
 
331
- ### Emoji Favicon
334
+ ## emojiFavicon
332
335
 
333
- Serves an SVG emoji as the application's favicon, providing a lightweight alternative to traditional favicon files.
336
+ A simple middleware that serves an emoji as an SVG favicon on `/favicon.ico`.
334
337
 
335
- **File:** `packages/core/src/base/middlewares/emoji-favicon.middleware.ts`
338
+ **Not exported from `@venizia/ignis`** - registered automatically by `BaseApplication`.
336
339
 
337
- #### Usage
340
+ ### Signature
338
341
 
339
342
  ```typescript
340
- import { emojiFavicon } from '@venizia/ignis';
343
+ function emojiFavicon(opts: { icon: string }): MiddlewareHandler
344
+ ```
341
345
 
342
- const app = new IgnisApplication({
343
- // ...
344
- });
346
+ | Parameter | Type | Description |
347
+ |-----------|------|-------------|
348
+ | `icon` | `string` | Emoji character to use as favicon |
349
+
350
+ ### Behavior
351
+
352
+ - Only intercepts requests to `/favicon.ico`
353
+ - Returns an SVG with `content-type: image/svg+xml`
354
+ - All other requests pass through via `next()`
355
+
356
+ **Default icon:** The application uses `this.configs.favicon ?? '🔥'` when registering.
345
357
 
346
- // Serve a rocket emoji as favicon
347
- app.use(emojiFavicon({ icon: '🚀' }));
358
+
359
+ ## Middleware Configuration via IApplicationConfigs
360
+
361
+ Several middleware behaviors are configured through `IApplicationConfigs`:
362
+
363
+ ```typescript
364
+ interface IApplicationConfigs {
365
+ favicon?: string; // Emoji for emojiFavicon (default: '🔥')
366
+ error?: { rootKey: string }; // Root key wrapper for appErrorHandler
367
+ asyncContext?: { enable: boolean }; // Enable Hono contextStorage() middleware
368
+ // ...
369
+ }
348
370
  ```
349
371
 
350
- #### How It Works
372
+ ## User-Defined Middlewares
351
373
 
352
- 1. Intercepts requests to `/favicon.ico`
353
- 2. Returns an inline SVG with the specified emoji
354
- 3. Sets `Content-Type: image/svg+xml`
355
- 4. All other requests pass through unchanged
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()`:
356
375
 
357
- #### API Reference
376
+ ```typescript
377
+ export class MyApplication extends BaseApplication {
378
+ async setupMiddlewares() {
379
+ const server = this.getServer();
358
380
 
359
- ##### `emojiFavicon(options)`
381
+ // CORS
382
+ server.use(cors({ origin: '*' }));
360
383
 
361
- **Parameters:**
362
- | Option | Type | Required | Description |
363
- |--------|------|----------|-------------|
364
- | `icon` | `string` | Yes | Emoji character to use as favicon |
384
+ // Body limit
385
+ server.use(bodyLimit({ maxSize: 1024 * 1024 })); // 1MB
365
386
 
366
- **Returns:** `MiddlewareHandler` - Hono middleware function
387
+ // Route-specific
388
+ server.use('/api/admin/*', adminAuthMiddleware());
389
+ }
390
+ }
391
+ ```
367
392
 
368
- #### Examples
393
+ The `IMiddlewareConfigs` type defines the shape for configurable middleware options:
369
394
 
370
395
  ```typescript
371
- // Different emoji icons
372
- app.use(emojiFavicon({ icon: '🔥' })); // Fire
373
- app.use(emojiFavicon({ icon: '⚡' })); // Lightning
374
- app.use(emojiFavicon({ icon: '🎯' })); // Target
375
- app.use(emojiFavicon({ icon: '🌟' })); // Star
396
+ interface IMiddlewareConfigs {
397
+ requestId?: IRequestIdOptions;
398
+ compress?: ICompressOptions;
399
+ cors?: ICORSOptions;
400
+ csrf?: ICSRFOptions;
401
+ bodyLimit?: IBodyLimitOptions;
402
+ ipRestriction?: IBaseMiddlewareOptions & IIPRestrictionRules;
403
+ [extra: string | symbol]: any;
404
+ }
376
405
  ```
377
406
 
378
- :::tip Browser Support
379
- SVG favicons are supported in all modern browsers. Fallback to a traditional `.ico` file if you need to support legacy browsers.
380
- :::
407
+ Each option interface extends `IBaseMiddlewareOptions`:
408
+
409
+ ```typescript
410
+ interface IBaseMiddlewareOptions {
411
+ enable: boolean;
412
+ path?: string;
413
+ [extra: string | symbol]: any;
414
+ }
415
+ ```
381
416
 
382
417
 
383
418
  ## Creating Custom Middleware
384
419
 
385
- IGNIS uses Hono's middleware system. Create custom middleware using the `createMiddleware` factory:
420
+ IGNIS uses Hono's middleware system. Create custom middleware using the `createMiddleware` factory from `hono/factory`.
386
421
 
387
422
  ### Basic Middleware
388
423
 
@@ -421,23 +456,18 @@ export const myMiddleware = (opts: MyMiddlewareOptions): MiddlewareHandler => {
421
456
  await next();
422
457
  });
423
458
  };
424
-
425
- // Usage
426
- app.use(myMiddleware({ enabled: true, prefix: 'API' }));
427
459
  ```
428
460
 
429
461
  ### Provider-Based Middleware
430
462
 
431
- For middleware requiring dependency injection:
463
+ For middleware requiring dependency injection, implement `IProvider<MiddlewareHandler>`:
432
464
 
433
465
  ```typescript
434
466
  import { BaseHelper } from '@venizia/ignis-helpers';
435
467
  import { IProvider } from '@venizia/ignis-inversion';
436
- import { injectable } from '@venizia/ignis-inversion';
437
468
  import { createMiddleware } from 'hono/factory';
438
469
  import type { MiddlewareHandler } from 'hono';
439
470
 
440
- @injectable()
441
471
  export class MyMiddleware extends BaseHelper implements IProvider<MiddlewareHandler> {
442
472
  constructor() {
443
473
  super({ scope: MyMiddleware.name });
@@ -450,119 +480,31 @@ export class MyMiddleware extends BaseHelper implements IProvider<MiddlewareHand
450
480
  });
451
481
  }
452
482
  }
453
-
454
- // Usage
455
- const myMiddleware = app.get(MyMiddleware);
456
- app.use(myMiddleware.value());
457
- ```
458
-
459
-
460
- ## Middleware Order & Priority
461
-
462
- Middleware execution order matters. Follow these guidelines:
463
-
464
- ### Recommended Order
465
-
466
- ```typescript
467
- const app = new IgnisApplication({ /* ... */ });
468
-
469
- // 1. CORS (if needed)
470
- app.use(cors());
471
-
472
- // 2. Request ID generation
473
- app.use(requestId());
474
-
475
- // 3. Request spy/logging (also handles body parsing)
476
- const requestSpy = new RequestSpyMiddleware();
477
- app.use(requestSpy.value());
478
-
479
- // 4. Security middleware (helmet, etc.)
480
- app.use(helmet());
481
-
482
- // 5. Rate limiting
483
- app.use(rateLimit());
484
-
485
- // 6. Authentication
486
- app.use('/api/*', authenticate());
487
-
488
- // 7. Favicon (can be early or late)
489
- app.use(emojiFavicon({ icon: '🚀' }));
490
-
491
- // 8. Application routes
492
- app.mountControllers();
493
-
494
- // 9. Error handler (LAST in chain)
495
- app.onError(appErrorHandler({ logger: app.logger }));
496
-
497
- // 10. Not found handler (AFTER error handler)
498
- app.notFound(notFoundHandler({ logger: app.logger }));
499
483
  ```
500
484
 
501
- ### Key Principles
502
-
503
- 1. **Request ID First**: Generate request ID before logging
504
- 2. **Request Spy Early**: Log and parse request bodies before business logic
505
- 3. **Security Middleware Before Routes**: Protect routes with security checks
506
- 4. **Error Handler Last**: Catch all errors from previous middleware
507
- 5. **404 Handler After Error Handler**: Ensure unhandled routes return 404
508
-
509
- :::warning Order Matters
510
- Placing error handler before routes will prevent it from catching route errors. Always register error handlers last.
511
- :::
512
-
513
-
514
- ## Common Patterns
515
-
516
- ### Conditional Middleware
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:
517
486
 
518
487
  ```typescript
519
- const app = new IgnisApplication({ /* ... */ });
488
+ export class MyApplication extends BaseApplication {
489
+ preConfigure() {
490
+ this.bind({ key: 'middlewares.MyMiddleware' })
491
+ .toProvider(MyMiddleware)
492
+ .setScope(BindingScopes.SINGLETON);
493
+ }
520
494
 
521
- // Enable request spy only in development
522
- if (process.env.NODE_ENV === 'development') {
523
- const requestSpy = new RequestSpyMiddleware();
524
- app.use(requestSpy.value());
495
+ async setupMiddlewares() {
496
+ const myMiddleware = this.get<MiddlewareHandler>({ key: 'middlewares.MyMiddleware' });
497
+ this.getServer().use(myMiddleware);
498
+ }
525
499
  }
526
500
  ```
527
501
 
528
- ### Route-Specific Middleware
529
-
530
- ```typescript
531
- // Apply middleware to specific routes
532
- app.use('/api/admin/*', adminAuthMiddleware());
533
- app.use('/api/public/*', rateLimitMiddleware());
534
- ```
535
-
536
- ### Middleware Composition
537
-
538
- ```typescript
539
- // Combine multiple middleware
540
- const apiMiddleware = (): MiddlewareHandler => {
541
- return createMiddleware(async (context, next) => {
542
- // Run multiple middleware in sequence
543
- await rateLimit()(context, async () => {
544
- await authenticate()(context, next);
545
- });
546
- });
547
- };
548
- ```
549
-
550
502
 
551
503
  ## Performance Considerations
552
504
 
553
505
  ### Request Spy in Production
554
506
 
555
- Request spy logs detailed information for every request. In high-traffic production environments:
556
-
557
- ```typescript
558
- // Conditional request spy
559
- const isDevelopment = process.env.NODE_ENV === 'development';
560
-
561
- if (isDevelopment) {
562
- const requestSpy = new RequestSpyMiddleware();
563
- app.use(requestSpy.value());
564
- }
565
- ```
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.
566
508
 
567
509
  ### Error Logging Volume
568
510
 
@@ -575,16 +517,12 @@ Error handlers log every error. For high error rates, consider:
575
517
  ## See Also
576
518
 
577
519
  - **Related References:**
578
- - [Application](./application.md) - Application setup and configuration
579
- - [Controllers](./controllers.md) - HTTP routing and request handling
580
- - [Dependency Injection](./dependency-injection.md) - DI container and providers
520
+ - [Application](./application.md) - Application lifecycle and initialization
521
+ - [Providers](./providers.md) - Provider pattern (`RequestSpyMiddleware` implements `IProvider`)
522
+ - [Components](./components.md) - `RequestTrackerComponent`
581
523
 
582
524
  - **Guides:**
583
- - [Building a CRUD API](/guides/tutorials/building-a-crud-api)
584
-
585
- - **Best Practices:**
586
- - [Troubleshooting Tips](/best-practices/troubleshooting-tips)
525
+ - [Application Guide](/guides/core-concepts/application/)
587
526
 
588
527
  - **External Resources:**
589
528
  - [Hono Middleware Documentation](https://hono.dev/docs/guides/middleware)
590
- - [HTTP Status Codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status)