@venizia/ignis-docs 0.0.8 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/content/best-practices/api-usage-examples.md +15 -12
- package/content/best-practices/architectural-patterns.md +70 -78
- package/content/best-practices/architecture-decisions.md +91 -60
- package/content/best-practices/code-style-standards/advanced-patterns.md +56 -44
- package/content/best-practices/code-style-standards/constants-configuration.md +11 -11
- package/content/best-practices/code-style-standards/control-flow.md +5 -2
- package/content/best-practices/code-style-standards/documentation.md +13 -13
- package/content/best-practices/code-style-standards/function-patterns.md +9 -10
- package/content/best-practices/code-style-standards/index.md +1 -1
- package/content/best-practices/code-style-standards/naming-conventions.md +10 -8
- package/content/best-practices/code-style-standards/route-definitions.md +30 -12
- package/content/best-practices/code-style-standards/tooling.md +8 -5
- package/content/best-practices/code-style-standards/type-safety.md +13 -12
- package/content/best-practices/common-pitfalls.md +56 -37
- package/content/best-practices/contribution-workflow.md +13 -14
- package/content/best-practices/data-modeling.md +46 -22
- package/content/best-practices/deployment-strategies.md +28 -27
- package/content/best-practices/error-handling.md +48 -24
- package/content/best-practices/index.md +5 -5
- package/content/best-practices/performance-optimization.md +40 -31
- package/content/best-practices/security-guidelines.md +52 -23
- package/content/best-practices/testing-strategies.md +65 -51
- package/content/best-practices/troubleshooting-tips.md +24 -24
- package/content/extensions/components/{swagger.md → api-reference.md} +40 -31
- package/content/extensions/components/authentication/api.md +19 -19
- package/content/extensions/components/authentication/errors.md +7 -7
- package/content/extensions/components/authentication/index.md +10 -8
- package/content/extensions/components/authentication/usage.md +101 -6
- package/content/extensions/components/authorization/api.md +45 -25
- package/content/extensions/components/authorization/errors.md +6 -6
- package/content/extensions/components/authorization/index.md +11 -10
- package/content/extensions/components/authorization/usage.md +21 -21
- package/content/extensions/components/health-check.md +1 -1
- package/content/extensions/components/index.md +5 -5
- package/content/extensions/components/mail/errors.md +15 -15
- package/content/extensions/components/mail/index.md +1 -2
- package/content/extensions/components/mail/usage.md +1 -1
- package/content/extensions/components/request-tracker.md +1 -1
- package/content/extensions/components/socket-io/api.md +9 -9
- package/content/extensions/components/socket-io/errors.md +5 -5
- package/content/extensions/components/socket-io/index.md +8 -8
- package/content/extensions/components/socket-io/usage.md +1 -1
- package/content/extensions/components/static-asset/api.md +17 -4
- package/content/extensions/components/static-asset/errors.md +4 -4
- package/content/extensions/components/static-asset/index.md +26 -28
- package/content/extensions/components/static-asset/usage.md +13 -12
- package/content/extensions/components/template/index.md +2 -2
- package/content/extensions/components/template/setup-page.md +1 -1
- package/content/extensions/components/websocket/api.md +3 -3
- package/content/extensions/components/websocket/errors.md +5 -5
- package/content/extensions/components/websocket/index.md +5 -5
- package/content/extensions/components/websocket/usage.md +3 -3
- package/content/extensions/helpers/cron/index.md +2 -2
- package/content/extensions/helpers/crypto/index.md +1 -1
- package/content/extensions/helpers/env/index.md +27 -12
- package/content/extensions/helpers/error/index.md +81 -25
- package/content/extensions/helpers/index.md +2 -3
- package/content/extensions/helpers/inversion/index.md +15 -7
- package/content/extensions/helpers/kafka/compile-binary.md +92 -0
- package/content/extensions/helpers/kafka/examples.md +1 -1
- package/content/extensions/helpers/kafka/index.md +3 -0
- package/content/extensions/helpers/logger/index.md +32 -2
- package/content/extensions/helpers/network/index.md +6 -0
- package/content/extensions/helpers/queue/index.md +14 -17
- package/content/extensions/helpers/redis/index.md +548 -323
- package/content/extensions/helpers/socket-io/index.md +14 -10
- package/content/extensions/helpers/storage/api.md +44 -8
- package/content/extensions/helpers/storage/index.md +43 -7
- package/content/extensions/helpers/template/index.md +6 -3
- package/content/extensions/helpers/types/index.md +11 -8
- package/content/extensions/helpers/websocket/api.md +9 -9
- package/content/extensions/helpers/websocket/index.md +7 -7
- package/content/extensions/helpers/worker-thread/index.md +2 -2
- package/content/extensions/index.md +3 -4
- package/content/extensions/src-details/mcp-server.md +18 -24
- package/content/guides/core-concepts/application/bootstrapping.md +11 -14
- package/content/guides/core-concepts/application/index.md +3 -3
- package/content/guides/core-concepts/components.md +19 -10
- package/content/guides/core-concepts/dependency-injection.md +6 -3
- package/content/guides/core-concepts/grpc-controllers.md +6 -5
- package/content/guides/core-concepts/persistent/datasources.md +42 -43
- package/content/guides/core-concepts/persistent/index.md +16 -7
- package/content/guides/core-concepts/persistent/models.md +24 -20
- package/content/guides/core-concepts/persistent/postgres-drivers.md +201 -0
- package/content/guides/core-concepts/persistent/repositories.md +40 -23
- package/content/guides/core-concepts/persistent/search-meilisearch.md +185 -0
- package/content/guides/core-concepts/persistent/search-typesense.md +431 -0
- package/content/guides/core-concepts/persistent/transactions.md +61 -25
- package/content/guides/core-concepts/rest-controllers.md +12 -9
- package/content/guides/core-concepts/services.md +330 -60
- package/content/guides/get-started/5-minute-quickstart.md +15 -15
- package/content/guides/get-started/philosophy.md +36 -36
- package/content/guides/get-started/setup.md +3 -3
- package/content/guides/index.md +3 -3
- package/content/guides/migrations/redis-helpers-migration.md +177 -0
- package/content/guides/migrations/scoped-rbac-migration.md +17 -17
- package/content/guides/migrations/unified-connectors-migration.md +113 -0
- package/content/guides/reference/glossary.md +19 -12
- package/content/guides/reference/mcp-docs-server.md +22 -18
- package/content/guides/tutorials/building-a-crud-api.md +37 -44
- package/content/guides/tutorials/complete-installation.md +17 -17
- package/content/guides/tutorials/ecommerce-api.md +163 -124
- package/content/guides/tutorials/realtime-chat.md +181 -135
- package/content/guides/tutorials/testing.md +65 -523
- package/content/index.md +2 -180
- package/content/public/apple-touch-icon.png +0 -0
- package/content/public/og-image.png +0 -0
- package/content/public/site.webmanifest +11 -0
- package/content/references/base/application.md +4 -5
- package/content/references/base/bootstrapping.md +18 -5
- package/content/references/base/components.md +149 -120
- package/content/references/base/connectors.md +178 -0
- package/content/references/base/controllers.md +41 -30
- package/content/references/base/datasources.md +163 -92
- package/content/references/base/dependency-injection.md +34 -22
- package/content/references/base/filter-system/application-usage.md +17 -14
- package/content/references/base/filter-system/array-operators.md +7 -2
- package/content/references/base/filter-system/comparison-operators.md +3 -0
- package/content/references/base/filter-system/default-filter.md +89 -71
- package/content/references/base/filter-system/fields-order-pagination.md +22 -22
- package/content/references/base/filter-system/index.md +6 -3
- package/content/references/base/filter-system/json-filtering.md +20 -1
- package/content/references/base/filter-system/list-operators.md +1 -1
- package/content/references/base/filter-system/logical-operators.md +33 -1
- package/content/references/base/filter-system/null-operators.md +30 -1
- package/content/references/base/filter-system/quick-reference.md +23 -4
- package/content/references/base/filter-system/tips.md +5 -5
- package/content/references/base/filter-system/use-cases.md +12 -12
- package/content/references/base/grpc-controllers.md +13 -13
- package/content/references/base/index.md +24 -12
- package/content/references/base/middlewares.md +265 -327
- package/content/references/base/models.md +63 -49
- package/content/references/base/providers.md +136 -130
- package/content/references/base/repositories/advanced.md +59 -58
- package/content/references/base/repositories/index.md +115 -91
- package/content/references/base/repositories/mixins.md +55 -291
- package/content/references/base/repositories/relations.md +54 -64
- package/content/references/base/repositories/soft-deletable.md +31 -30
- package/content/references/base/services.md +296 -93
- package/content/references/configuration/environment-variables.md +49 -31
- package/content/references/configuration/index.md +6 -6
- package/content/references/index.md +17 -12
- package/content/references/quick-reference.md +65 -106
- package/content/references/utilities/crypto.md +65 -23
- package/content/references/utilities/index.md +3 -3
- package/content/references/utilities/jsx.md +6 -4
- package/content/references/utilities/module.md +68 -20
- package/content/references/utilities/parse.md +4 -14
- package/content/references/utilities/promise.md +9 -7
- package/content/references/utilities/schema.md +5 -3
- package/dist/mcp-server/common/guards.d.ts +8 -0
- package/dist/mcp-server/common/guards.d.ts.map +1 -0
- package/dist/mcp-server/common/guards.js +14 -0
- package/dist/mcp-server/common/guards.js.map +1 -0
- package/dist/mcp-server/common/index.d.ts +1 -0
- package/dist/mcp-server/common/index.d.ts.map +1 -1
- package/dist/mcp-server/common/index.js +1 -0
- package/dist/mcp-server/common/index.js.map +1 -1
- package/dist/mcp-server/helpers/docs.helper.d.ts.map +1 -1
- package/dist/mcp-server/helpers/docs.helper.js +4 -2
- package/dist/mcp-server/helpers/docs.helper.js.map +1 -1
- package/dist/mcp-server/helpers/github.helper.js +1 -1
- package/dist/mcp-server/index.js +7 -2
- package/dist/mcp-server/index.js.map +1 -1
- package/dist/mcp-server/tools/base.tool.d.ts +6 -2
- package/dist/mcp-server/tools/base.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/base.tool.js.map +1 -1
- package/dist/mcp-server/tools/docs/search-documents.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/search-code.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/search-code.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/search-code.tool.js +4 -1
- package/dist/mcp-server/tools/github/search-code.tool.js.map +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js +3 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js.map +1 -1
- package/package.json +9 -9
- package/content/extensions/helpers/testing/index.md +0 -510
- package/content/references/base/middleware.md +0 -347
|
@@ -1,111 +1,110 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Middlewares Reference
|
|
3
|
-
description: Technical reference for
|
|
3
|
+
description: Technical reference for built-in middlewares in IGNIS
|
|
4
4
|
difficulty: intermediate
|
|
5
|
-
lastUpdated: 2026-
|
|
5
|
+
lastUpdated: 2026-06-14
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Middlewares Reference
|
|
9
9
|
|
|
10
|
-
IGNIS provides
|
|
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
|
|
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
|
-
|
|
18
|
-
- [
|
|
19
|
-
-
|
|
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 |
|
|
24
|
-
|
|
25
|
-
| `appErrorHandler` |
|
|
26
|
-
| `notFoundHandler` |
|
|
27
|
-
| `RequestSpyMiddleware` |
|
|
28
|
-
| `emojiFavicon` | Serves an emoji as favicon |
|
|
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
|
-
##
|
|
34
|
+
## Default Registration Order
|
|
31
35
|
|
|
32
|
-
|
|
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
|
-
|
|
38
|
+
```typescript
|
|
39
|
+
protected async registerDefaultMiddlewares() {
|
|
40
|
+
const server = this.getServer();
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
// 1. Global error handler
|
|
43
|
+
server.onError(appErrorHandler({ logger, rootKey }));
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
// 2. Async context storage (if enabled)
|
|
46
|
+
if (this.configs.asyncContext?.enable) {
|
|
47
|
+
server.use(contextStorage());
|
|
48
|
+
}
|
|
45
49
|
|
|
46
|
-
|
|
50
|
+
// 3. Not-found handler
|
|
51
|
+
server.notFound(notFoundHandler({ logger }));
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
// 4. RequestTrackerComponent (requestId + RequestSpyMiddleware)
|
|
54
|
+
this.component(RequestTrackerComponent);
|
|
49
55
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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
|
-
|
|
60
|
-
import { appErrorHandler } from '@venizia/ignis';
|
|
63
|
+
## appErrorHandler
|
|
61
64
|
|
|
62
|
-
|
|
63
|
-
// ...
|
|
64
|
-
});
|
|
65
|
+
Global error handler registered via `server.onError()`. Handles ZodError validation errors, PostgreSQL constraint violations, and generic errors.
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
app.onError(appErrorHandler({
|
|
68
|
-
logger: app.logger
|
|
69
|
-
}));
|
|
70
|
-
```
|
|
67
|
+
**Not exported from `@venizia/ignis`** - registered automatically by `BaseApplication`.
|
|
71
68
|
|
|
72
|
-
|
|
69
|
+
### Signature
|
|
73
70
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
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
|
|
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": "
|
|
94
|
+
"messageCode": "user.email.invalid",
|
|
97
95
|
"statusCode": 422,
|
|
98
|
-
"requestId": "
|
|
96
|
+
"requestId": "abc-123",
|
|
99
97
|
"details": {
|
|
100
|
-
"url": "http://localhost:3000/
|
|
101
|
-
"path": "/
|
|
102
|
-
"stack": "...",
|
|
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": "
|
|
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, {
|
|
119
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
145
|
-
| `22` Data exception | `22001` string too long
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
145
|
+
#### 3. Generic Errors
|
|
155
146
|
|
|
156
|
-
|
|
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
|
-
|
|
149
|
+
### Response Format
|
|
162
150
|
|
|
163
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
208
|
+
## notFoundHandler
|
|
201
209
|
|
|
202
|
-
|
|
210
|
+
Returns a JSON 404 response when no route matches. Registered via `server.notFound()`.
|
|
203
211
|
|
|
204
|
-
**
|
|
212
|
+
**Not exported from `@venizia/ignis`** - registered automatically by `BaseApplication`.
|
|
205
213
|
|
|
206
|
-
|
|
214
|
+
### Signature
|
|
207
215
|
|
|
208
216
|
```typescript
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
227
|
-
"
|
|
231
|
+
"statusCode": 404,
|
|
232
|
+
"requestId": "abc-123",
|
|
233
|
+
"path": "/unknown",
|
|
234
|
+
"url": "http://localhost:3000/unknown"
|
|
228
235
|
}
|
|
229
236
|
```
|
|
230
237
|
|
|
231
|
-
|
|
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
|
-
|
|
241
|
+
## RequestSpyMiddleware
|
|
236
242
|
|
|
237
|
-
|
|
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
|
-
**
|
|
245
|
+
**Not exported from `@venizia/ignis`** - registered automatically via `RequestTrackerComponent` by `BaseApplication`.
|
|
243
246
|
|
|
247
|
+
### Class Definition
|
|
244
248
|
|
|
245
|
-
|
|
249
|
+
```typescript
|
|
250
|
+
export class RequestSpyMiddleware extends BaseHelper implements IProvider<MiddlewareHandler> {
|
|
251
|
+
static readonly REQUEST_ID_KEY = 'requestId';
|
|
246
252
|
|
|
247
|
-
|
|
253
|
+
constructor() {
|
|
254
|
+
super({ scope: 'SpyMW' });
|
|
255
|
+
}
|
|
248
256
|
|
|
249
|
-
|
|
257
|
+
async parseBody(opts: { req: TContext['req'] }): Promise<unknown>;
|
|
258
|
+
value(): MiddlewareHandler;
|
|
259
|
+
}
|
|
260
|
+
```
|
|
250
261
|
|
|
251
|
-
|
|
262
|
+
### How It Is Registered
|
|
252
263
|
|
|
253
|
-
|
|
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
|
-
|
|
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
|
-
|
|
266
|
-
import { RequestSpyMiddleware } from '@venizia/ignis';
|
|
270
|
+
### Request Logging
|
|
267
271
|
|
|
268
|
-
|
|
269
|
-
// ...
|
|
270
|
-
});
|
|
272
|
+
In **non-production** mode, logs the full request including query and body:
|
|
271
273
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
app.use(requestSpy.value());
|
|
274
|
+
```
|
|
275
|
+
[requestId][clientIp][=>] METHOD /path | query: {...} | body: {...}
|
|
275
276
|
```
|
|
276
277
|
|
|
277
|
-
|
|
278
|
+
In **production** mode, body is excluded:
|
|
278
279
|
|
|
279
|
-
**Request Start:**
|
|
280
280
|
```
|
|
281
|
-
[
|
|
281
|
+
[requestId][clientIp][=>] METHOD /path | query: {...}
|
|
282
282
|
```
|
|
283
283
|
|
|
284
|
-
|
|
284
|
+
### Response Logging
|
|
285
|
+
|
|
286
|
+
After the handler completes:
|
|
287
|
+
|
|
285
288
|
```
|
|
286
|
-
[
|
|
289
|
+
[requestId][clientIp][<=] METHOD /path | Took: 12.34 (ms)
|
|
287
290
|
```
|
|
288
291
|
|
|
289
|
-
|
|
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
|
-
|
|
305
|
+
### IP Detection
|
|
292
306
|
|
|
293
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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(
|
|
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
|
-
|
|
334
|
+
## emojiFavicon
|
|
332
335
|
|
|
333
|
-
|
|
336
|
+
A simple middleware that serves an emoji as an SVG favicon on `/favicon.ico`.
|
|
334
337
|
|
|
335
|
-
**
|
|
338
|
+
**Not exported from `@venizia/ignis`** - registered automatically by `BaseApplication`.
|
|
336
339
|
|
|
337
|
-
|
|
340
|
+
### Signature
|
|
338
341
|
|
|
339
342
|
```typescript
|
|
340
|
-
|
|
343
|
+
function emojiFavicon(opts: { icon: string }): MiddlewareHandler
|
|
344
|
+
```
|
|
341
345
|
|
|
342
|
-
|
|
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
|
-
|
|
347
|
-
|
|
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
|
-
|
|
372
|
+
## User-Defined Middlewares
|
|
351
373
|
|
|
352
|
-
|
|
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
|
-
|
|
376
|
+
```typescript
|
|
377
|
+
export class MyApplication extends BaseApplication {
|
|
378
|
+
async setupMiddlewares() {
|
|
379
|
+
const server = this.getServer();
|
|
358
380
|
|
|
359
|
-
|
|
381
|
+
// CORS
|
|
382
|
+
server.use(cors({ origin: '*' }));
|
|
360
383
|
|
|
361
|
-
|
|
362
|
-
|
|
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
|
-
|
|
387
|
+
// Route-specific
|
|
388
|
+
server.use('/api/admin/*', adminAuthMiddleware());
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
```
|
|
367
392
|
|
|
368
|
-
|
|
393
|
+
The `IMiddlewareConfigs` type defines the shape for configurable middleware options:
|
|
369
394
|
|
|
370
395
|
```typescript
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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
|
-
|
|
379
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
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
|
-
|
|
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
|
|
579
|
-
- [
|
|
580
|
-
- [
|
|
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
|
-
- [
|
|
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)
|