@venizia/ignis-docs 0.0.3 → 0.0.4-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 +1 -1
- package/package.json +4 -2
- package/wiki/best-practices/api-usage-examples.md +591 -0
- package/wiki/best-practices/architectural-patterns.md +415 -0
- package/wiki/best-practices/architecture-decisions.md +488 -0
- package/wiki/{get-started/best-practices → best-practices}/code-style-standards.md +406 -17
- package/wiki/{get-started/best-practices → best-practices}/common-pitfalls.md +109 -4
- package/wiki/{get-started/best-practices → best-practices}/contribution-workflow.md +34 -7
- package/wiki/best-practices/data-modeling.md +376 -0
- package/wiki/best-practices/deployment-strategies.md +698 -0
- package/wiki/best-practices/index.md +27 -0
- package/wiki/best-practices/performance-optimization.md +196 -0
- package/wiki/best-practices/security-guidelines.md +218 -0
- package/wiki/{get-started/best-practices → best-practices}/troubleshooting-tips.md +97 -1
- package/wiki/changelogs/2025-12-16-initial-architecture.md +1 -1
- package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +1 -1
- package/wiki/changelogs/2025-12-17-refactor.md +1 -1
- package/wiki/changelogs/2025-12-18-performance-optimizations.md +5 -5
- package/wiki/changelogs/2025-12-18-repository-validation-security.md +13 -7
- package/wiki/changelogs/2025-12-26-nested-relations-and-generics.md +2 -2
- package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
- package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
- package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
- package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
- package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
- package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
- package/wiki/changelogs/index.md +6 -0
- package/wiki/changelogs/planned-schema-migrator.md +0 -8
- package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
- package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
- package/wiki/guides/core-concepts/components-guide.md +509 -0
- package/wiki/{get-started → guides}/core-concepts/components.md +24 -17
- package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
- package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
- package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
- package/wiki/guides/core-concepts/persistent/index.md +119 -0
- package/wiki/guides/core-concepts/persistent/models.md +241 -0
- package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
- package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
- package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
- package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
- package/wiki/guides/get-started/philosophy.md +682 -0
- package/wiki/guides/get-started/setup.md +157 -0
- package/wiki/guides/index.md +89 -0
- package/wiki/guides/reference/glossary.md +243 -0
- package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
- package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
- package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
- package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
- package/wiki/guides/tutorials/realtime-chat.md +1261 -0
- package/wiki/guides/tutorials/testing.md +723 -0
- package/wiki/index.md +176 -37
- package/wiki/references/base/application.md +27 -0
- package/wiki/references/base/bootstrapping.md +30 -26
- package/wiki/references/base/components.md +24 -7
- package/wiki/references/base/controllers.md +51 -20
- package/wiki/references/base/datasources.md +30 -0
- package/wiki/references/base/dependency-injection.md +39 -3
- package/wiki/references/base/filter-system/application-usage.md +224 -0
- package/wiki/references/base/filter-system/array-operators.md +132 -0
- package/wiki/references/base/filter-system/comparison-operators.md +109 -0
- package/wiki/references/base/filter-system/default-filter.md +428 -0
- package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
- package/wiki/references/base/filter-system/index.md +127 -0
- package/wiki/references/base/filter-system/json-filtering.md +197 -0
- package/wiki/references/base/filter-system/list-operators.md +71 -0
- package/wiki/references/base/filter-system/logical-operators.md +156 -0
- package/wiki/references/base/filter-system/null-operators.md +58 -0
- package/wiki/references/base/filter-system/pattern-matching.md +108 -0
- package/wiki/references/base/filter-system/quick-reference.md +431 -0
- package/wiki/references/base/filter-system/range-operators.md +63 -0
- package/wiki/references/base/filter-system/tips.md +190 -0
- package/wiki/references/base/filter-system/use-cases.md +452 -0
- package/wiki/references/base/index.md +90 -0
- package/wiki/references/base/middlewares.md +602 -0
- package/wiki/references/base/models.md +215 -23
- package/wiki/references/base/providers.md +732 -0
- package/wiki/references/base/repositories/advanced.md +555 -0
- package/wiki/references/base/repositories/index.md +228 -0
- package/wiki/references/base/repositories/mixins.md +331 -0
- package/wiki/references/base/repositories/relations.md +486 -0
- package/wiki/references/base/repositories.md +40 -635
- package/wiki/references/base/services.md +28 -4
- package/wiki/references/components/authentication.md +22 -2
- package/wiki/references/components/health-check.md +12 -0
- package/wiki/references/components/index.md +23 -0
- package/wiki/references/components/mail.md +687 -0
- package/wiki/references/components/request-tracker.md +16 -0
- package/wiki/references/components/socket-io.md +18 -0
- package/wiki/references/components/static-asset.md +14 -26
- package/wiki/references/components/swagger.md +17 -0
- package/wiki/references/configuration/environment-variables.md +427 -0
- package/wiki/references/configuration/index.md +73 -0
- package/wiki/references/helpers/cron.md +14 -0
- package/wiki/references/helpers/crypto.md +15 -0
- package/wiki/references/helpers/env.md +16 -0
- package/wiki/references/helpers/error.md +17 -0
- package/wiki/references/helpers/index.md +14 -0
- package/wiki/references/helpers/inversion.md +24 -4
- package/wiki/references/helpers/logger.md +19 -0
- package/wiki/references/helpers/network.md +11 -0
- package/wiki/references/helpers/queue.md +19 -0
- package/wiki/references/helpers/redis.md +21 -0
- package/wiki/references/helpers/socket-io.md +24 -5
- package/wiki/references/helpers/storage.md +18 -10
- package/wiki/references/helpers/testing.md +18 -0
- package/wiki/references/helpers/types.md +16 -0
- package/wiki/references/helpers/uid.md +167 -0
- package/wiki/references/helpers/worker-thread.md +16 -0
- package/wiki/references/index.md +177 -0
- package/wiki/references/quick-reference.md +634 -0
- package/wiki/references/src-details/boot.md +3 -3
- package/wiki/references/src-details/dev-configs.md +0 -4
- package/wiki/references/src-details/docs.md +2 -2
- package/wiki/references/src-details/index.md +86 -0
- package/wiki/references/src-details/inversion.md +1 -6
- package/wiki/references/src-details/mcp-server.md +3 -15
- package/wiki/references/utilities/index.md +86 -10
- package/wiki/references/utilities/jsx.md +577 -0
- package/wiki/references/utilities/request.md +0 -2
- package/wiki/references/utilities/statuses.md +740 -0
- package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
- package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
- package/wiki/get-started/best-practices/data-modeling.md +0 -177
- package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
- package/wiki/get-started/best-practices/performance-optimization.md +0 -97
- package/wiki/get-started/best-practices/security-guidelines.md +0 -99
- package/wiki/get-started/core-concepts/persistent.md +0 -539
- package/wiki/get-started/index.md +0 -65
- package/wiki/get-started/philosophy.md +0 -296
- package/wiki/get-started/prerequisites.md +0 -113
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Base Abstractions
|
|
2
|
+
|
|
3
|
+
Core classes that power every Ignis application - from the Application entry point to Repositories for data access.
|
|
4
|
+
|
|
5
|
+
## Quick Reference
|
|
6
|
+
|
|
7
|
+
| Class | Purpose | Extends |
|
|
8
|
+
|-------|---------|---------|
|
|
9
|
+
| `BaseApplication` | Application entry point, DI container | `AbstractApplication` |
|
|
10
|
+
| `BaseController` | HTTP route handlers | - |
|
|
11
|
+
| `BaseService` | Business logic layer | - |
|
|
12
|
+
| `BaseProvider` | Factory pattern for runtime instantiation | `BaseHelper` |
|
|
13
|
+
| `BaseComponent` | Pluggable feature modules | - |
|
|
14
|
+
| `BaseDataSource` | Database connections | - |
|
|
15
|
+
| `BaseEntity` | Model definitions | - |
|
|
16
|
+
| `DefaultCRUDRepository` | Full CRUD operations | `PersistableRepository` |
|
|
17
|
+
| `ReadableRepository` | Read-only operations | `AbstractRepository` |
|
|
18
|
+
|
|
19
|
+
## Architecture
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
23
|
+
│ BaseApplication │
|
|
24
|
+
│ (DI Container + Lifecycle + Server Management) │
|
|
25
|
+
├─────────────────────────────────────────────────────────────┤
|
|
26
|
+
│ │
|
|
27
|
+
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
28
|
+
│ │ BaseController│ │ BaseService │ │BaseComponent │ │
|
|
29
|
+
│ │ (HTTP Layer) │ │(Business Logic)│ │ (Plugins) │ │
|
|
30
|
+
│ └──────┬───────┘ └──────┬───────┘ └──────────────┘ │
|
|
31
|
+
│ │ │ │
|
|
32
|
+
│ └────────┬─────────┘ │
|
|
33
|
+
│ ▼ │
|
|
34
|
+
│ ┌──────────────────┐ │
|
|
35
|
+
│ │DefaultCRUDRepository│ │
|
|
36
|
+
│ │ (Data Access) │ │
|
|
37
|
+
│ └────────┬──────────┘ │
|
|
38
|
+
│ │ │
|
|
39
|
+
│ ┌────────┴────────┐ │
|
|
40
|
+
│ ▼ ▼ │
|
|
41
|
+
│ ┌────────────┐ ┌────────────┐ │
|
|
42
|
+
│ │BaseDataSource│ │ BaseEntity │ │
|
|
43
|
+
│ │(Connection) │ │ (Schema) │ │
|
|
44
|
+
│ └─────────────┘ └────────────┘ │
|
|
45
|
+
│ │
|
|
46
|
+
└──────────────────────────────────────────────────────────────┘
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## What's in This Section
|
|
50
|
+
|
|
51
|
+
### Core Application
|
|
52
|
+
- [Application](./application.md) - `BaseApplication` class, resource registration, lifecycle hooks
|
|
53
|
+
- [Bootstrapping](./bootstrapping.md) - Startup sequence, `initialize()` flow
|
|
54
|
+
|
|
55
|
+
### HTTP Layer
|
|
56
|
+
- [Controllers](./controllers.md) - Route handlers, decorators, request/response handling
|
|
57
|
+
- [Middlewares](./middlewares.md) - Built-in middlewares for error handling, logging, and request processing
|
|
58
|
+
- [Services](./services.md) - Business logic, injectable services
|
|
59
|
+
|
|
60
|
+
### Dependency Injection
|
|
61
|
+
- [Dependency Injection](./dependency-injection.md) - Container, bindings, `@inject` patterns
|
|
62
|
+
- [Providers](./providers.md) - Factory pattern for configuration-driven instantiation
|
|
63
|
+
- [Components](./components.md) - Pluggable modules, component lifecycle
|
|
64
|
+
|
|
65
|
+
### Data Layer
|
|
66
|
+
- [Models & Enrichers](./models.md) - `BaseEntity`, schema definitions, enrichers
|
|
67
|
+
- [DataSources](./datasources.md) - Database connections, auto-discovery
|
|
68
|
+
- [Repositories](./repositories/) - CRUD operations, filtering, relations
|
|
69
|
+
- [Filter System](./filter-system/) - Query filter types and operators
|
|
70
|
+
|
|
71
|
+
## Class Hierarchy
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
AbstractApplication
|
|
75
|
+
└── BaseApplication ──────► Your Application
|
|
76
|
+
|
|
77
|
+
AbstractRepository
|
|
78
|
+
├── ReadableRepository
|
|
79
|
+
│ └── PersistableRepository
|
|
80
|
+
│ └── DefaultCRUDRepository ──────► Your Repository
|
|
81
|
+
│
|
|
82
|
+
BaseController ──────► Your Controller
|
|
83
|
+
BaseService ──────► Your Service
|
|
84
|
+
BaseProvider ──────► Your Provider
|
|
85
|
+
BaseComponent ──────► Your Component
|
|
86
|
+
BaseDataSource ──────► Your DataSource
|
|
87
|
+
BaseEntity ──────► Your Model
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
> **Related:** [Core Concepts Guide](../../guides/core-concepts/application/) | [Persistent Layer Guide](../../guides/core-concepts/persistent/)
|
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Middlewares Reference
|
|
3
|
+
description: Technical reference for IGNIS built-in middlewares
|
|
4
|
+
difficulty: intermediate
|
|
5
|
+
lastUpdated: 2026-01-03
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Middlewares Reference
|
|
9
|
+
|
|
10
|
+
IGNIS provides a collection of built-in middlewares for common application needs including error handling, request logging, request normalization, and favicon serving.
|
|
11
|
+
|
|
12
|
+
**Files:**
|
|
13
|
+
- `packages/core/src/base/middlewares/*.ts`
|
|
14
|
+
|
|
15
|
+
## Prerequisites
|
|
16
|
+
|
|
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
|
+
|
|
21
|
+
## Quick Reference
|
|
22
|
+
|
|
23
|
+
| Middleware | Purpose | Key Options |
|
|
24
|
+
|------------|---------|-------------|
|
|
25
|
+
| `appErrorHandler` | Catches and formats application errors | `logger` |
|
|
26
|
+
| `notFoundHandler` | Handles 404 Not Found responses | `logger` |
|
|
27
|
+
| `requestNormalize` | Pre-parses JSON request bodies | None |
|
|
28
|
+
| `RequestSpyMiddleware` | Logs request lifecycle and timing | None |
|
|
29
|
+
| `emojiFavicon` | Serves an emoji as favicon | `icon` |
|
|
30
|
+
|
|
31
|
+
## Table of Contents
|
|
32
|
+
|
|
33
|
+
- [Error Handler (`appErrorHandler`)](#error-handler-apporerrorhandler)
|
|
34
|
+
- [Not Found Handler (`notFoundHandler`)](#not-found-handler-notfoundhandler)
|
|
35
|
+
- [Request Normalizer (`requestNormalize`)](#request-normalizer-requestnormalize)
|
|
36
|
+
- [Request Spy (Debug)](#request-spy-debug)
|
|
37
|
+
- [Emoji Favicon](#emoji-favicon)
|
|
38
|
+
- [Creating Custom Middleware](#creating-custom-middleware)
|
|
39
|
+
- [Middleware Order & Priority](#middleware-order--priority)
|
|
40
|
+
- [See Also](#see-also)
|
|
41
|
+
|
|
42
|
+
## Built-in Middlewares
|
|
43
|
+
|
|
44
|
+
### Error Handler (`appErrorHandler`)
|
|
45
|
+
|
|
46
|
+
The error handler middleware catches all unhandled errors in your application and formats them into consistent JSON responses.
|
|
47
|
+
|
|
48
|
+
**File:** `packages/core/src/base/middlewares/app-error.middleware.ts`
|
|
49
|
+
|
|
50
|
+
#### Features
|
|
51
|
+
|
|
52
|
+
- **Automatic Error Formatting**: Converts all errors to structured JSON responses
|
|
53
|
+
- **ZodError Support**: Special handling for Zod validation errors with detailed field-level messages
|
|
54
|
+
- **Environment-Aware**: Hides stack traces and error causes in production
|
|
55
|
+
- **Request Tracking**: Includes `requestId` for debugging and tracing
|
|
56
|
+
- **Status Code Detection**: Automatically extracts HTTP status codes from errors
|
|
57
|
+
|
|
58
|
+
#### Usage
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { appErrorHandler } from '@venizia/ignis';
|
|
62
|
+
|
|
63
|
+
const app = new IgnisApplication({
|
|
64
|
+
// ...
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Register error handler
|
|
68
|
+
app.onError(appErrorHandler({
|
|
69
|
+
logger: app.logger
|
|
70
|
+
}));
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### Error Response Format
|
|
74
|
+
|
|
75
|
+
**Standard Error:**
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"message": "Something went wrong",
|
|
79
|
+
"statusCode": 500,
|
|
80
|
+
"requestId": "abc123",
|
|
81
|
+
"details": {
|
|
82
|
+
"url": "http://localhost:3000/api/users",
|
|
83
|
+
"path": "/api/users",
|
|
84
|
+
"stack": "Error: Something went wrong\n at ...", // development only
|
|
85
|
+
"cause": { ... } // development only
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Validation Error (ZodError):**
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"message": "ValidationError",
|
|
94
|
+
"statusCode": 422,
|
|
95
|
+
"requestId": "abc123",
|
|
96
|
+
"details": {
|
|
97
|
+
"url": "http://localhost:3000/api/users",
|
|
98
|
+
"path": "/api/users",
|
|
99
|
+
"stack": "...", // development only
|
|
100
|
+
"cause": [
|
|
101
|
+
{
|
|
102
|
+
"path": "email",
|
|
103
|
+
"message": "Invalid email address",
|
|
104
|
+
"code": "invalid_string",
|
|
105
|
+
"expected": "string",
|
|
106
|
+
"received": "undefined"
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### API Reference
|
|
114
|
+
|
|
115
|
+
##### `appErrorHandler(options)`
|
|
116
|
+
|
|
117
|
+
**Parameters:**
|
|
118
|
+
| Option | Type | Required | Description |
|
|
119
|
+
|--------|------|----------|-------------|
|
|
120
|
+
| `logger` | `ApplicationLogger` | Yes | Logger instance for error logging |
|
|
121
|
+
|
|
122
|
+
**Returns:** `ErrorHandler` - Hono error handler function
|
|
123
|
+
|
|
124
|
+
#### Common Patterns
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
// Custom error with status code
|
|
128
|
+
class NotFoundError extends Error {
|
|
129
|
+
statusCode = 404;
|
|
130
|
+
|
|
131
|
+
constructor(message: string) {
|
|
132
|
+
super(message);
|
|
133
|
+
this.name = 'NotFoundError';
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Throw in controller
|
|
138
|
+
const GetUserConfig = {
|
|
139
|
+
method: HTTP.Methods.GET,
|
|
140
|
+
path: '/users/:id',
|
|
141
|
+
request: {
|
|
142
|
+
params: z.object({ id: z.string() }),
|
|
143
|
+
},
|
|
144
|
+
responses: jsonResponse({
|
|
145
|
+
schema: z.object({ id: z.string(), name: z.string() }),
|
|
146
|
+
}),
|
|
147
|
+
} as const;
|
|
148
|
+
|
|
149
|
+
@get({ configs: GetUserConfig })
|
|
150
|
+
async getUser(c: TRouteContext<typeof GetUserConfig>) {
|
|
151
|
+
const { id } = c.req.valid('param');
|
|
152
|
+
const user = await this.userRepository.findById(id);
|
|
153
|
+
if (!user) {
|
|
154
|
+
throw new NotFoundError(`User ${id} not found`);
|
|
155
|
+
}
|
|
156
|
+
return c.json(user, HTTP.ResultCodes.RS_2.Ok);
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
### Not Found Handler (`notFoundHandler`)
|
|
163
|
+
|
|
164
|
+
Handles requests to routes that don't exist, returning a standardized 404 response.
|
|
165
|
+
|
|
166
|
+
**File:** `packages/core/src/base/middlewares/not-found.middleware.ts`
|
|
167
|
+
|
|
168
|
+
#### Usage
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import { notFoundHandler } from '@venizia/ignis';
|
|
172
|
+
|
|
173
|
+
const app = new IgnisApplication({
|
|
174
|
+
// ...
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Register 404 handler
|
|
178
|
+
app.notFound(notFoundHandler({
|
|
179
|
+
logger: app.logger
|
|
180
|
+
}));
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### Response Format
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"message": "URL NOT FOUND",
|
|
188
|
+
"path": "/api/nonexistent",
|
|
189
|
+
"url": "http://localhost:3000/api/nonexistent"
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Status Code:** `404 Not Found`
|
|
194
|
+
|
|
195
|
+
#### API Reference
|
|
196
|
+
|
|
197
|
+
##### `notFoundHandler(options)`
|
|
198
|
+
|
|
199
|
+
**Parameters:**
|
|
200
|
+
| Option | Type | Required | Default | Description |
|
|
201
|
+
|--------|------|----------|---------|-------------|
|
|
202
|
+
| `logger` | `ApplicationLogger` | No | `console` | Logger instance for logging 404s |
|
|
203
|
+
|
|
204
|
+
**Returns:** `NotFoundHandler` - Hono not found handler function
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
### Request Normalizer (`requestNormalize`)
|
|
209
|
+
|
|
210
|
+
Pre-parses JSON request bodies to ensure consistent request handling and prevent common parsing issues.
|
|
211
|
+
|
|
212
|
+
**File:** `packages/core/src/base/middlewares/request-normalize.middleware.ts`
|
|
213
|
+
|
|
214
|
+
#### How It Works
|
|
215
|
+
|
|
216
|
+
1. **Skip for GET/OPTIONS**: No normalization for read-only requests
|
|
217
|
+
2. **Check Content-Length**: Skip if no body is present (`Content-Length: 0`)
|
|
218
|
+
3. **Check Content-Type**: Only process `application/json` requests
|
|
219
|
+
4. **Pre-parse JSON**: Calls `context.req.json()` to cache the parsed body
|
|
220
|
+
|
|
221
|
+
#### Benefits
|
|
222
|
+
|
|
223
|
+
- Prevents multiple body parsing attempts
|
|
224
|
+
- Ensures body is available for all subsequent middleware/handlers
|
|
225
|
+
- Catches JSON parsing errors early in the request lifecycle
|
|
226
|
+
|
|
227
|
+
#### Usage
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
import { requestNormalize } from '@venizia/ignis';
|
|
231
|
+
|
|
232
|
+
const app = new IgnisApplication({
|
|
233
|
+
// ...
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Register as early middleware
|
|
237
|
+
app.use(requestNormalize());
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
:::tip Why Pre-parse?
|
|
241
|
+
Hono's request body can only be read once. This middleware ensures the body is parsed and cached early, making it available to all downstream handlers.
|
|
242
|
+
:::
|
|
243
|
+
|
|
244
|
+
#### API Reference
|
|
245
|
+
|
|
246
|
+
##### `requestNormalize()`
|
|
247
|
+
|
|
248
|
+
**Parameters:** None
|
|
249
|
+
|
|
250
|
+
**Returns:** `MiddlewareHandler` - Hono middleware function
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
### Request Spy (Debug)
|
|
255
|
+
|
|
256
|
+
Logs detailed information about each request including timing, IP address, method, path, and query parameters.
|
|
257
|
+
|
|
258
|
+
**File:** `packages/core/src/base/middlewares/request-spy.middleware.ts`
|
|
259
|
+
|
|
260
|
+
#### Features
|
|
261
|
+
|
|
262
|
+
- Request lifecycle logging (START/DONE)
|
|
263
|
+
- Performance timing tracking
|
|
264
|
+
- IP address extraction (supports `x-real-ip` and `x-forwarded-for` headers)
|
|
265
|
+
- Request ID tracking
|
|
266
|
+
- Query and body parameter logging
|
|
267
|
+
|
|
268
|
+
#### Usage
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
import { RequestSpyMiddleware } from '@venizia/ignis';
|
|
272
|
+
|
|
273
|
+
const app = new IgnisApplication({
|
|
274
|
+
// ...
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Create and register spy middleware
|
|
278
|
+
const requestSpy = new RequestSpyMiddleware();
|
|
279
|
+
app.use(requestSpy.value());
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
#### Log Output
|
|
283
|
+
|
|
284
|
+
**Request Start:**
|
|
285
|
+
```
|
|
286
|
+
[spy][abc123] START | Handling Request | forwardedIp: 192.168.1.1 | path: /api/users | method: GET
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**Request Complete:**
|
|
290
|
+
```
|
|
291
|
+
[spy][abc123] DONE | Handling Request | forwardedIp: 192.168.1.1 | path: /api/users | method: GET | Took: 45.23 (ms)
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
#### API Reference
|
|
295
|
+
|
|
296
|
+
##### `RequestSpyMiddleware`
|
|
297
|
+
|
|
298
|
+
**Class Methods:**
|
|
299
|
+
| Method | Returns | Description |
|
|
300
|
+
|--------|---------|-------------|
|
|
301
|
+
| `value()` | `MiddlewareHandler` | Returns the middleware handler |
|
|
302
|
+
|
|
303
|
+
**Static Properties:**
|
|
304
|
+
| Property | Type | Value | Description |
|
|
305
|
+
|----------|------|-------|-------------|
|
|
306
|
+
| `REQUEST_ID_KEY` | `string` | `'requestId'` | Context key for request ID |
|
|
307
|
+
|
|
308
|
+
#### Accessing Request ID
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
import { RequestSpyMiddleware, get, HTTP, jsonResponse, TRouteContext, z } from '@venizia/ignis';
|
|
312
|
+
|
|
313
|
+
const ExampleConfig = {
|
|
314
|
+
method: HTTP.Methods.GET,
|
|
315
|
+
path: '/example',
|
|
316
|
+
responses: jsonResponse({
|
|
317
|
+
schema: z.object({ requestId: z.string() }),
|
|
318
|
+
}),
|
|
319
|
+
} as const;
|
|
320
|
+
|
|
321
|
+
// In a controller
|
|
322
|
+
@get({ configs: ExampleConfig })
|
|
323
|
+
async example(c: TRouteContext<typeof ExampleConfig>) {
|
|
324
|
+
const requestId = c.get(RequestSpyMiddleware.REQUEST_ID_KEY);
|
|
325
|
+
console.log('Request ID:', requestId);
|
|
326
|
+
return c.json({ requestId }, HTTP.ResultCodes.RS_2.Ok);
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
:::warning Performance Impact
|
|
331
|
+
Request spy logs every request detail. Consider disabling or reducing verbosity in production environments with high traffic.
|
|
332
|
+
:::
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
### Emoji Favicon
|
|
337
|
+
|
|
338
|
+
Serves an SVG emoji as the application's favicon, providing a lightweight alternative to traditional favicon files.
|
|
339
|
+
|
|
340
|
+
**File:** `packages/core/src/base/middlewares/emoji-favicon.middleware.ts`
|
|
341
|
+
|
|
342
|
+
#### Usage
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
import { emojiFavicon } from '@venizia/ignis';
|
|
346
|
+
|
|
347
|
+
const app = new IgnisApplication({
|
|
348
|
+
// ...
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// Serve a rocket emoji as favicon
|
|
352
|
+
app.use(emojiFavicon({ icon: '🚀' }));
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
#### How It Works
|
|
356
|
+
|
|
357
|
+
1. Intercepts requests to `/favicon.ico`
|
|
358
|
+
2. Returns an inline SVG with the specified emoji
|
|
359
|
+
3. Sets `Content-Type: image/svg+xml`
|
|
360
|
+
4. All other requests pass through unchanged
|
|
361
|
+
|
|
362
|
+
#### API Reference
|
|
363
|
+
|
|
364
|
+
##### `emojiFavicon(options)`
|
|
365
|
+
|
|
366
|
+
**Parameters:**
|
|
367
|
+
| Option | Type | Required | Description |
|
|
368
|
+
|--------|------|----------|-------------|
|
|
369
|
+
| `icon` | `string` | Yes | Emoji character to use as favicon |
|
|
370
|
+
|
|
371
|
+
**Returns:** `MiddlewareHandler` - Hono middleware function
|
|
372
|
+
|
|
373
|
+
#### Examples
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
// Different emoji icons
|
|
377
|
+
app.use(emojiFavicon({ icon: '🔥' })); // Fire
|
|
378
|
+
app.use(emojiFavicon({ icon: '⚡' })); // Lightning
|
|
379
|
+
app.use(emojiFavicon({ icon: '🎯' })); // Target
|
|
380
|
+
app.use(emojiFavicon({ icon: '🌟' })); // Star
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
:::tip Browser Support
|
|
384
|
+
SVG favicons are supported in all modern browsers. Fallback to a traditional `.ico` file if you need to support legacy browsers.
|
|
385
|
+
:::
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
## Creating Custom Middleware
|
|
390
|
+
|
|
391
|
+
IGNIS uses Hono's middleware system. Create custom middleware using the `createMiddleware` factory:
|
|
392
|
+
|
|
393
|
+
### Basic Middleware
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
import { createMiddleware } from 'hono/factory';
|
|
397
|
+
import type { MiddlewareHandler } from 'hono';
|
|
398
|
+
|
|
399
|
+
export const myMiddleware = (): MiddlewareHandler => {
|
|
400
|
+
return createMiddleware(async (context, next) => {
|
|
401
|
+
// Before request handling
|
|
402
|
+
console.log('Before:', context.req.path);
|
|
403
|
+
|
|
404
|
+
await next();
|
|
405
|
+
|
|
406
|
+
// After request handling
|
|
407
|
+
console.log('After:', context.req.path);
|
|
408
|
+
});
|
|
409
|
+
};
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Middleware with Options
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
interface MyMiddlewareOptions {
|
|
416
|
+
enabled: boolean;
|
|
417
|
+
prefix?: string;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export const myMiddleware = (opts: MyMiddlewareOptions): MiddlewareHandler => {
|
|
421
|
+
const { enabled, prefix = 'LOG' } = opts;
|
|
422
|
+
|
|
423
|
+
return createMiddleware(async (context, next) => {
|
|
424
|
+
if (enabled) {
|
|
425
|
+
console.log(`[${prefix}]`, context.req.path);
|
|
426
|
+
}
|
|
427
|
+
await next();
|
|
428
|
+
});
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
// Usage
|
|
432
|
+
app.use(myMiddleware({ enabled: true, prefix: 'API' }));
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### Provider-Based Middleware
|
|
436
|
+
|
|
437
|
+
For middleware requiring dependency injection:
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
import { BaseHelper } from '@venizia/ignis-helpers';
|
|
441
|
+
import { IProvider } from '@venizia/ignis-inversion';
|
|
442
|
+
import { injectable } from '@venizia/ignis-inversion';
|
|
443
|
+
import { createMiddleware } from 'hono/factory';
|
|
444
|
+
import type { MiddlewareHandler } from 'hono';
|
|
445
|
+
|
|
446
|
+
@injectable()
|
|
447
|
+
export class MyMiddleware extends BaseHelper implements IProvider<MiddlewareHandler> {
|
|
448
|
+
constructor() {
|
|
449
|
+
super({ scope: MyMiddleware.name });
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
value(): MiddlewareHandler {
|
|
453
|
+
return createMiddleware(async (context, next) => {
|
|
454
|
+
this.logger.info('Processing request:', context.req.path);
|
|
455
|
+
await next();
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Usage
|
|
461
|
+
const myMiddleware = app.get(MyMiddleware);
|
|
462
|
+
app.use(myMiddleware.value());
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
---
|
|
466
|
+
|
|
467
|
+
## Middleware Order & Priority
|
|
468
|
+
|
|
469
|
+
Middleware execution order matters. Follow these guidelines:
|
|
470
|
+
|
|
471
|
+
### Recommended Order
|
|
472
|
+
|
|
473
|
+
```typescript
|
|
474
|
+
const app = new IgnisApplication({ /* ... */ });
|
|
475
|
+
|
|
476
|
+
// 1. CORS (if needed)
|
|
477
|
+
app.use(cors());
|
|
478
|
+
|
|
479
|
+
// 2. Request ID generation
|
|
480
|
+
app.use(requestId());
|
|
481
|
+
|
|
482
|
+
// 3. Request spy/logging
|
|
483
|
+
const requestSpy = new RequestSpyMiddleware();
|
|
484
|
+
app.use(requestSpy.value());
|
|
485
|
+
|
|
486
|
+
// 4. Request normalization
|
|
487
|
+
app.use(requestNormalize());
|
|
488
|
+
|
|
489
|
+
// 5. Security middleware (helmet, etc.)
|
|
490
|
+
app.use(helmet());
|
|
491
|
+
|
|
492
|
+
// 6. Rate limiting
|
|
493
|
+
app.use(rateLimit());
|
|
494
|
+
|
|
495
|
+
// 7. Authentication
|
|
496
|
+
app.use('/api/*', authenticate());
|
|
497
|
+
|
|
498
|
+
// 8. Favicon (can be early or late)
|
|
499
|
+
app.use(emojiFavicon({ icon: '🚀' }));
|
|
500
|
+
|
|
501
|
+
// 9. Application routes
|
|
502
|
+
app.mountControllers();
|
|
503
|
+
|
|
504
|
+
// 10. Error handler (LAST in chain)
|
|
505
|
+
app.onError(appErrorHandler({ logger: app.logger }));
|
|
506
|
+
|
|
507
|
+
// 11. Not found handler (AFTER error handler)
|
|
508
|
+
app.notFound(notFoundHandler({ logger: app.logger }));
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### Key Principles
|
|
512
|
+
|
|
513
|
+
1. **Request ID First**: Generate request ID before logging
|
|
514
|
+
2. **Logging Early**: Log requests before normalization/parsing
|
|
515
|
+
3. **Normalization Before Business Logic**: Parse bodies before they're needed
|
|
516
|
+
4. **Security Middleware Before Routes**: Protect routes with security checks
|
|
517
|
+
5. **Error Handler Last**: Catch all errors from previous middleware
|
|
518
|
+
6. **404 Handler After Error Handler**: Ensure unhandled routes return 404
|
|
519
|
+
|
|
520
|
+
:::warning Order Matters
|
|
521
|
+
Placing error handler before routes will prevent it from catching route errors. Always register error handlers last.
|
|
522
|
+
:::
|
|
523
|
+
|
|
524
|
+
---
|
|
525
|
+
|
|
526
|
+
## Common Patterns
|
|
527
|
+
|
|
528
|
+
### Conditional Middleware
|
|
529
|
+
|
|
530
|
+
```typescript
|
|
531
|
+
const app = new IgnisApplication({ /* ... */ });
|
|
532
|
+
|
|
533
|
+
// Enable request spy only in development
|
|
534
|
+
if (process.env.NODE_ENV === 'development') {
|
|
535
|
+
const requestSpy = new RequestSpyMiddleware();
|
|
536
|
+
app.use(requestSpy.value());
|
|
537
|
+
}
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### Route-Specific Middleware
|
|
541
|
+
|
|
542
|
+
```typescript
|
|
543
|
+
// Apply middleware to specific routes
|
|
544
|
+
app.use('/api/admin/*', adminAuthMiddleware());
|
|
545
|
+
app.use('/api/public/*', rateLimitMiddleware());
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
### Middleware Composition
|
|
549
|
+
|
|
550
|
+
```typescript
|
|
551
|
+
// Combine multiple middleware
|
|
552
|
+
const apiMiddleware = (): MiddlewareHandler => {
|
|
553
|
+
return createMiddleware(async (context, next) => {
|
|
554
|
+
// Run multiple middleware in sequence
|
|
555
|
+
await requestNormalize()(context, async () => {
|
|
556
|
+
await authenticate()(context, next);
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
};
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
---
|
|
563
|
+
|
|
564
|
+
## Performance Considerations
|
|
565
|
+
|
|
566
|
+
### Request Spy in Production
|
|
567
|
+
|
|
568
|
+
Request spy logs detailed information for every request. In high-traffic production environments:
|
|
569
|
+
|
|
570
|
+
```typescript
|
|
571
|
+
// Conditional request spy
|
|
572
|
+
const isDevelopment = process.env.NODE_ENV === 'development';
|
|
573
|
+
|
|
574
|
+
if (isDevelopment) {
|
|
575
|
+
const requestSpy = new RequestSpyMiddleware();
|
|
576
|
+
app.use(requestSpy.value());
|
|
577
|
+
}
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
### Error Logging Volume
|
|
581
|
+
|
|
582
|
+
Error handlers log every error. For high error rates, consider:
|
|
583
|
+
- Sampling (log 1 in N errors)
|
|
584
|
+
- Error aggregation services (Sentry, Rollbar)
|
|
585
|
+
- Rate-limited logging
|
|
586
|
+
|
|
587
|
+
---
|
|
588
|
+
|
|
589
|
+
## See Also
|
|
590
|
+
|
|
591
|
+
- **Related References:**
|
|
592
|
+
- [Application](./application.md) - Application setup and configuration
|
|
593
|
+
- [Controllers](./controllers.md) - HTTP routing and request handling
|
|
594
|
+
- [Dependency Injection](./dependency-injection.md) - DI container and providers
|
|
595
|
+
|
|
596
|
+
- **Guides:**
|
|
597
|
+
- [Building Your First API](/guides/getting-started/first-api.md)
|
|
598
|
+
- [Error Handling Best Practices](/best-practices/architecture/error-handling.md)
|
|
599
|
+
|
|
600
|
+
- **External Resources:**
|
|
601
|
+
- [Hono Middleware Documentation](https://hono.dev/docs/guides/middleware)
|
|
602
|
+
- [HTTP Status Codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status)
|