@venizia/ignis-docs 0.2.0 → 0.2.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.
- package/README.md +14 -14
- package/content/best-practices/api-usage-examples.md +39 -19
- package/content/best-practices/architectural-patterns.md +22 -11
- package/content/best-practices/architecture-decisions.md +29 -16
- package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
- package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
- package/content/best-practices/code-style-standards/control-flow.md +33 -1
- package/content/best-practices/code-style-standards/documentation.md +28 -8
- package/content/best-practices/code-style-standards/function-patterns.md +15 -4
- package/content/best-practices/code-style-standards/index.md +7 -2
- package/content/best-practices/code-style-standards/naming-conventions.md +26 -2
- package/content/best-practices/code-style-standards/route-definitions.md +9 -5
- package/content/best-practices/code-style-standards/tooling.md +14 -1
- package/content/best-practices/code-style-standards/type-safety.md +39 -6
- package/content/best-practices/common-pitfalls.md +59 -35
- package/content/best-practices/contribution-workflow.md +6 -2
- package/content/best-practices/data-modeling.md +78 -62
- package/content/best-practices/deployment-strategies.md +56 -19
- package/content/best-practices/error-handling.md +182 -93
- package/content/best-practices/index.md +6 -0
- package/content/best-practices/performance-optimization.md +26 -13
- package/content/best-practices/security-guidelines.md +35 -9
- package/content/best-practices/testing-strategies.md +7 -2
- package/content/best-practices/troubleshooting-tips.md +27 -17
- package/content/extensions/components/api-reference.md +107 -322
- package/content/extensions/components/authentication/api.md +454 -603
- package/content/extensions/components/authentication/errors.md +121 -498
- package/content/extensions/components/authentication/index.md +88 -801
- package/content/extensions/components/authentication/usage.md +207 -956
- package/content/extensions/components/authorization/api.md +736 -656
- package/content/extensions/components/authorization/errors.md +168 -206
- package/content/extensions/components/authorization/index.md +82 -797
- package/content/extensions/components/authorization/usage.md +194 -527
- package/content/extensions/components/health-check.md +71 -243
- package/content/extensions/components/mail/api.md +504 -287
- package/content/extensions/components/mail/errors.md +73 -61
- package/content/extensions/components/mail/index.md +96 -467
- package/content/extensions/components/mail/usage.md +130 -172
- package/content/extensions/components/request-tracker.md +66 -173
- package/content/extensions/components/socket-io/api.md +195 -13
- package/content/extensions/components/socket-io/errors.md +3 -3
- package/content/extensions/components/socket-io/index.md +50 -337
- package/content/extensions/components/socket-io/usage.md +143 -26
- package/content/extensions/components/static-asset/api.md +410 -142
- package/content/extensions/components/static-asset/errors.md +110 -53
- package/content/extensions/components/static-asset/index.md +79 -608
- package/content/extensions/components/static-asset/usage.md +180 -300
- package/content/extensions/components/websocket/api.md +275 -399
- package/content/extensions/components/websocket/errors.md +47 -56
- package/content/extensions/components/websocket/index.md +74 -407
- package/content/extensions/components/websocket/usage.md +110 -341
- package/content/extensions/helpers/cron/index.md +51 -160
- package/content/extensions/helpers/crypto/index.md +62 -483
- package/content/extensions/helpers/crypto/reference.md +456 -0
- package/content/extensions/helpers/env/index.md +60 -178
- package/content/extensions/helpers/error/index.md +221 -207
- package/content/extensions/helpers/inversion/index.md +65 -556
- package/content/extensions/helpers/inversion/reference.md +522 -0
- package/content/extensions/helpers/kafka/admin.md +20 -1
- package/content/extensions/helpers/kafka/compile-binary.md +41 -35
- package/content/extensions/helpers/kafka/consumer.md +54 -20
- package/content/extensions/helpers/kafka/examples.md +21 -16
- package/content/extensions/helpers/kafka/index.md +80 -610
- package/content/extensions/helpers/kafka/producer.md +134 -5
- package/content/extensions/helpers/kafka/schema-registry.md +45 -70
- package/content/extensions/helpers/logger/hf-logger.md +193 -0
- package/content/extensions/helpers/logger/index.md +64 -563
- package/content/extensions/helpers/logger/pino.md +85 -0
- package/content/extensions/helpers/logger/reference.md +746 -0
- package/content/extensions/helpers/network/api.md +241 -195
- package/content/extensions/helpers/network/index.md +72 -530
- package/content/extensions/helpers/queue/index.md +72 -900
- package/content/extensions/helpers/queue/reference.md +467 -0
- package/content/extensions/helpers/redis/index.md +73 -645
- package/content/extensions/helpers/redis/reference.md +727 -0
- package/content/extensions/helpers/secrets/index.md +66 -0
- package/content/extensions/helpers/socket-io/api.md +305 -203
- package/content/extensions/helpers/socket-io/index.md +66 -432
- package/content/extensions/helpers/storage/api.md +564 -462
- package/content/extensions/helpers/storage/index.md +77 -573
- package/content/extensions/helpers/types/index.md +66 -499
- package/content/extensions/helpers/types/reference.md +650 -0
- package/content/extensions/helpers/uid/index.md +58 -227
- package/content/extensions/helpers/websocket/api.md +329 -216
- package/content/extensions/helpers/websocket/index.md +65 -503
- package/content/extensions/helpers/worker-thread/index.md +58 -396
- package/content/extensions/helpers/worker-thread/reference.md +428 -0
- package/content/guides/core-concepts/persistent/models.md +1 -1
- package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
- package/content/guides/core-concepts/persistent/transactions.md +1 -1
- package/content/guides/core-concepts/secrets-vault.md +177 -0
- package/content/guides/core-concepts/services.md +1 -1
- package/content/guides/migrations/redis-helpers-migration.md +1 -1
- package/content/guides/migrations/unified-connectors-migration.md +2 -2
- package/content/guides/tutorials/ecommerce-api.md +3 -8
- package/content/references/base/application.md +1 -1
- package/content/references/base/connectors.md +79 -136
- package/content/references/base/datasources-reference.md +599 -0
- package/content/references/base/datasources.md +84 -444
- package/content/references/base/dependency-injection.md +17 -39
- package/content/references/base/filter-system/application-usage.md +69 -121
- package/content/references/base/filter-system/array-operators.md +12 -0
- package/content/references/base/filter-system/comparison-operators.md +12 -0
- package/content/references/base/filter-system/default-filter.md +136 -348
- package/content/references/base/filter-system/fields-order-pagination.md +38 -16
- package/content/references/base/filter-system/index.md +106 -257
- package/content/references/base/filter-system/json-filtering.md +12 -2
- package/content/references/base/filter-system/list-operators.md +16 -2
- package/content/references/base/filter-system/logical-operators.md +13 -0
- package/content/references/base/filter-system/null-operators.md +13 -0
- package/content/references/base/filter-system/pattern-matching.md +12 -0
- package/content/references/base/filter-system/quick-reference.md +11 -2
- package/content/references/base/filter-system/range-operators.md +12 -0
- package/content/references/base/filter-system/tips.md +70 -133
- package/content/references/base/filter-system/use-cases.md +156 -233
- package/content/references/base/middlewares.md +35 -21
- package/content/references/base/models-reference.md +886 -0
- package/content/references/base/models.md +80 -1452
- package/content/references/base/repositories/advanced.md +156 -192
- package/content/references/base/repositories/index.md +77 -650
- package/content/references/base/repositories/mixins.md +22 -18
- package/content/references/base/repositories/relations.md +123 -171
- package/content/references/base/repositories/soft-deletable.md +58 -56
- package/content/references/base/secrets.md +263 -0
- package/content/references/base/services.md +2 -2
- package/content/references/configuration/environment-variables.md +48 -4
- package/content/references/configuration/index.md +49 -31
- package/content/references/quick-reference.md +3 -16
- package/content/references/utilities/crypto.md +35 -76
- package/content/references/utilities/date.md +33 -73
- package/content/references/utilities/index.md +1 -1
- package/content/references/utilities/jsx-reference.md +298 -0
- package/content/references/utilities/jsx.md +82 -525
- package/content/references/utilities/module.md +29 -62
- package/content/references/utilities/parse.md +34 -64
- package/content/references/utilities/performance.md +33 -58
- package/content/references/utilities/promise.md +28 -62
- package/content/references/utilities/request.md +57 -218
- package/content/references/utilities/schema.md +43 -137
- package/content/references/utilities/statuses-reference.md +361 -0
- package/content/references/utilities/statuses.md +63 -667
- package/package.json +8 -8
|
@@ -1,245 +1,84 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
The `parseMultipartBody` function is an asynchronous utility for parsing `multipart/form-data` request bodies, which is essential for handling file uploads. It can store the uploaded files in memory or on disk.
|
|
8
|
-
|
|
9
|
-
### `parseMultipartBody(opts)`
|
|
10
|
-
|
|
11
|
-
- `opts` (object):
|
|
12
|
-
- `context` (object with `req` property): The Hono context object for the current request. Uses `context.req.formData()` internally.
|
|
13
|
-
- `storage` (`'memory'` | `'disk'`, optional): The storage strategy for uploaded files. Defaults to `'memory'`.
|
|
14
|
-
- `uploadDir` (string, optional): The directory to save files to when using the `'disk'` storage strategy. Defaults to `'./uploads'`. The directory is created recursively if it does not exist.
|
|
15
|
-
|
|
16
|
-
The function returns a `Promise` that resolves to an array of `IParsedFile` objects. String form fields are skipped (only `File` entries are processed).
|
|
17
|
-
|
|
18
|
-
### `IParsedFile` Interface
|
|
1
|
+
---
|
|
2
|
+
title: Request Utility
|
|
3
|
+
description: Multipart form parsing and secure Content-Disposition header helpers for file uploads and downloads
|
|
4
|
+
difficulty: beginner
|
|
5
|
+
lastUpdated: 2026-07-16
|
|
6
|
+
---
|
|
19
7
|
|
|
20
|
-
|
|
21
|
-
- `originalname`: The original name of the uploaded file.
|
|
22
|
-
- `encoding`: The file's encoding (always `'utf8'`).
|
|
23
|
-
- `mimetype`: The MIME type of the file.
|
|
24
|
-
- `size`: The size of the file in bytes.
|
|
25
|
-
- `buffer` (Buffer, optional): The file's content as a Buffer (if `storage` is `'memory'`).
|
|
26
|
-
- `filename` (string, optional): The generated name of the file on disk (if `storage` is `'disk'`). Format: `{timestamp}-{randomString}-{sanitizedOriginalName}`.
|
|
27
|
-
- `path` (string, optional): The full path to the file on disk (if `storage` is `'disk'`).
|
|
8
|
+
# Request Utility
|
|
28
9
|
|
|
29
|
-
|
|
10
|
+
Functions for handling HTTP request data: parsing `multipart/form-data` bodies for file uploads, and building safe, RFC-compliant `Content-Disposition` headers for downloads.
|
|
30
11
|
|
|
31
|
-
|
|
12
|
+
## In one example
|
|
32
13
|
|
|
33
14
|
```typescript
|
|
34
15
|
import { BaseRestController, controller } from '@venizia/ignis';
|
|
35
|
-
import { parseMultipartBody, HTTP } from '@venizia/ignis-helpers';
|
|
16
|
+
import { parseMultipartBody, createContentDispositionHeader, HTTP } from '@venizia/ignis-helpers';
|
|
36
17
|
|
|
37
18
|
@controller({ path: '/files' })
|
|
38
19
|
export class FileController extends BaseRestController {
|
|
39
|
-
// ...
|
|
40
20
|
override binding() {
|
|
41
|
-
this.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
21
|
+
this.bindRoute({ configs: { path: '/upload', method: 'post' } }).to({
|
|
22
|
+
handler: async (ctx) => {
|
|
23
|
+
const files = await parseMultipartBody({ context: ctx, storage: 'disk', uploadDir: './uploads' });
|
|
24
|
+
return ctx.json(
|
|
25
|
+
{ message: 'Uploaded', files: files.map(f => ({ name: f.originalname, size: f.size })) },
|
|
26
|
+
HTTP.ResultCodes.RS_2.Ok,
|
|
27
|
+
);
|
|
47
28
|
},
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return c.json(
|
|
59
|
-
{ message: `${files.length} file(s) uploaded successfully.` },
|
|
60
|
-
HTTP.ResultCodes.RS_2.Ok,
|
|
61
|
-
);
|
|
62
|
-
} catch (error) {
|
|
63
|
-
return c.json(
|
|
64
|
-
{ message: 'Failed to upload files', error: error.message },
|
|
65
|
-
HTTP.ResultCodes.RS_5.InternalServerError,
|
|
66
|
-
);
|
|
67
|
-
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
this.bindRoute({ configs: { path: '/:filename', method: 'get' } }).to({
|
|
32
|
+
handler: (ctx) => {
|
|
33
|
+
const { filename } = ctx.req.valid('param');
|
|
34
|
+
ctx.header(
|
|
35
|
+
'content-disposition',
|
|
36
|
+
createContentDispositionHeader({ filename, type: 'attachment' }),
|
|
37
|
+
);
|
|
38
|
+
// ... stream the file
|
|
68
39
|
},
|
|
69
40
|
});
|
|
70
41
|
}
|
|
71
42
|
}
|
|
72
43
|
```
|
|
73
44
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
## Content-Disposition Utilities
|
|
77
|
-
|
|
78
|
-
These utilities help create secure, RFC-compliant `Content-Disposition` headers for file downloads.
|
|
79
|
-
|
|
80
|
-
### `createContentDispositionHeader`
|
|
81
|
-
|
|
82
|
-
Creates a safe Content-Disposition header with proper filename encoding for file downloads.
|
|
83
|
-
|
|
84
|
-
#### `createContentDispositionHeader(opts)`
|
|
85
|
-
|
|
86
|
-
- `opts` (object):
|
|
87
|
-
- `filename` (string): The filename to use in the Content-Disposition header.
|
|
88
|
-
- `type` (`'attachment'` | `'inline'`): The disposition type.
|
|
89
|
-
|
|
90
|
-
The function returns a properly formatted `Content-Disposition` header string with both ASCII and UTF-8 encoded filenames for maximum browser compatibility.
|
|
91
|
-
|
|
92
|
-
**Features:**
|
|
93
|
-
- Automatic filename sanitization via `sanitizeFilename()`
|
|
94
|
-
- UTF-8 encoding support via `encodeRFC5987()`
|
|
95
|
-
- RFC 5987 compliant
|
|
96
|
-
- Dual `filename` / `filename*` for browser compatibility
|
|
97
|
-
|
|
98
|
-
**Example:**
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
import { createContentDispositionHeader } from '@venizia/ignis-helpers';
|
|
102
|
-
|
|
103
|
-
// Attachment (file download)
|
|
104
|
-
ctx.header('content-disposition', createContentDispositionHeader({
|
|
105
|
-
filename: 'my-document.pdf',
|
|
106
|
-
type: 'attachment',
|
|
107
|
-
}));
|
|
108
|
-
// Output: attachment; filename="my-document.pdf"; filename*=UTF-8''my-document.pdf
|
|
109
|
-
|
|
110
|
-
// Inline (display in browser)
|
|
111
|
-
ctx.header('content-disposition', createContentDispositionHeader({
|
|
112
|
-
filename: 'report.pdf',
|
|
113
|
-
type: 'inline',
|
|
114
|
-
}));
|
|
115
|
-
// Output: inline; filename="report.pdf"; filename*=UTF-8''report.pdf
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
---
|
|
119
|
-
|
|
120
|
-
### `sanitizeFilename`
|
|
121
|
-
|
|
122
|
-
Sanitizes a filename for safe use, removing path components and dangerous characters. Useful for HTTP headers (e.g., Content-Disposition) and general file handling.
|
|
123
|
-
|
|
124
|
-
#### `sanitizeFilename(filename: string): string`
|
|
125
|
-
|
|
126
|
-
- `filename` (string): The filename to sanitize.
|
|
127
|
-
|
|
128
|
-
Returns a safe filename suitable for use in headers or filesystem operations.
|
|
129
|
-
|
|
130
|
-
**Features:**
|
|
131
|
-
- Removes path components via `path.basename()` (prevents directory traversal attacks)
|
|
132
|
-
- Allows only word characters (`\w`), spaces, hyphens, underscores, and dots
|
|
133
|
-
- Replaces dangerous characters with underscores
|
|
134
|
-
- Removes leading dots (prevents hidden files)
|
|
135
|
-
- Replaces consecutive dots with a single dot
|
|
136
|
-
- Removes ".." patterns (additional path traversal protection)
|
|
137
|
-
- Returns `'download'` for empty, suspicious, or invalid filenames
|
|
138
|
-
|
|
139
|
-
**Example:**
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
import { sanitizeFilename } from '@venizia/ignis-helpers';
|
|
143
|
-
|
|
144
|
-
sanitizeFilename('../../etc/passwd'); // Returns: 'passwd'
|
|
145
|
-
sanitizeFilename('my<file>name.txt'); // Returns: 'my_file_name.txt'
|
|
146
|
-
sanitizeFilename('.hidden'); // Returns: 'hidden'
|
|
147
|
-
sanitizeFilename('file...txt'); // Returns: 'file.txt'
|
|
148
|
-
sanitizeFilename(''); // Returns: 'download'
|
|
149
|
-
sanitizeFilename('..'); // Returns: 'download'
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
### `encodeRFC5987`
|
|
154
|
-
|
|
155
|
-
Encodes a filename according to RFC 5987 for use in HTTP headers. Encodes using `encodeURIComponent` and additionally escapes single quotes, parentheses, and asterisks.
|
|
156
|
-
|
|
157
|
-
#### `encodeRFC5987(filename: string): string`
|
|
158
|
-
|
|
159
|
-
- `filename` (string): The filename to encode.
|
|
160
|
-
|
|
161
|
-
Returns an RFC 5987 encoded string suitable for the `filename*` parameter in Content-Disposition headers.
|
|
162
|
-
|
|
163
|
-
**Example:**
|
|
164
|
-
|
|
165
|
-
```typescript
|
|
166
|
-
import { encodeRFC5987 } from '@venizia/ignis-helpers';
|
|
167
|
-
|
|
168
|
-
encodeRFC5987('my document.pdf'); // Returns: 'my%20document.pdf'
|
|
169
|
-
```
|
|
170
|
-
|
|
45
|
+
## Functions
|
|
171
46
|
|
|
172
|
-
|
|
47
|
+
| Function | Signature | What it does |
|
|
48
|
+
|----------|-----------|---------------|
|
|
49
|
+
| `parseMultipartBody` | `parseMultipartBody(opts: { context: { req: any }; storage?: 'memory' \| 'disk'; uploadDir?: string }): Promise<IParsedFile[]>` | Parses a `multipart/form-data` body via `context.req.formData()`. String fields are skipped - only `File` entries are returned. |
|
|
50
|
+
| `sanitizeFilename` | `sanitizeFilename(filename: string): string` | Strips path components and dangerous characters from `filename`. Returns `'download'` for empty or suspicious input. |
|
|
51
|
+
| `encodeRFC5987` | `encodeRFC5987(filename: string): string` | RFC 5987 encodes `filename` for the `filename*` header parameter (`encodeURIComponent` plus escaped `'`, `(`, `)`, `*`). |
|
|
52
|
+
| `createContentDispositionHeader` | `createContentDispositionHeader(opts: { filename: string; type: 'attachment' \| 'inline' }): string` | Builds a full `Content-Disposition` value: sanitizes the filename, then emits both the ASCII `filename=` and UTF-8 `filename*=` forms. |
|
|
173
53
|
|
|
174
|
-
|
|
54
|
+
## Parsed file shape
|
|
175
55
|
|
|
176
|
-
|
|
177
|
-
- `url` (string): The request URL.
|
|
178
|
-
- `method` (string): The HTTP method.
|
|
179
|
-
- `[extra: string | symbol]`: Additional arbitrary properties.
|
|
56
|
+
`parseMultipartBody` resolves to an array of objects (the `IParsedFile` shape, internal to the module - not separately exported):
|
|
180
57
|
|
|
58
|
+
| Field | Type | Present when |
|
|
59
|
+
|-------|------|---------------|
|
|
60
|
+
| `fieldname` | `string` | always |
|
|
61
|
+
| `originalname` | `string` | always |
|
|
62
|
+
| `encoding` | `string` | always - hardcoded `'utf8'` |
|
|
63
|
+
| `mimetype` | `string` | always |
|
|
64
|
+
| `size` | `number` | always |
|
|
65
|
+
| `buffer` | `Buffer` | `storage: 'memory'` (default) |
|
|
66
|
+
| `filename` | `string` | `storage: 'disk'` - format `{timestamp}-{randomString}-{sanitizedOriginalName}` |
|
|
67
|
+
| `path` | `string` | `storage: 'disk'` |
|
|
181
68
|
|
|
182
|
-
##
|
|
69
|
+
## Notes
|
|
183
70
|
|
|
184
|
-
|
|
71
|
+
- **`storage` defaults to `'memory'`**; `uploadDir` defaults to `'./uploads'` and is created recursively if it does not exist.
|
|
72
|
+
- **`sanitizeFilename` is applied automatically** inside `createContentDispositionHeader` - callers do not need to sanitize twice. It also removes leading dots, collapses repeated dots, and strips `..` sequences to block directory traversal and hidden-file tricks.
|
|
73
|
+
- **`createContentDispositionHeader` always emits both forms** (`filename="..."; filename*=UTF-8''...`) for maximum browser compatibility - older browsers read the ASCII fallback, modern ones read the UTF-8 form.
|
|
74
|
+
- **`IRequestedRemark`** is a separately exported interface for describing a request: `{ id: string; url: string; method: string; [extra: string | symbol]: any }`. It is not consumed internally by `parseMultipartBody` or any other function on this page - it is a general-purpose shape for application code that needs to tag a request with an id, URL, method, and arbitrary extra fields.
|
|
185
75
|
|
|
186
|
-
|
|
187
|
-
import { BaseRestController, controller } from '@venizia/ignis';
|
|
188
|
-
import { parseMultipartBody, createContentDispositionHeader, HTTP } from '@venizia/ignis-helpers';
|
|
189
|
-
import fs from 'node:fs';
|
|
190
|
-
import path from 'node:path';
|
|
76
|
+
## See also
|
|
191
77
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
// Upload endpoint
|
|
196
|
-
this.bindRoute({
|
|
197
|
-
configs: { path: '/upload', method: 'post' },
|
|
198
|
-
}).to({
|
|
199
|
-
handler: async (ctx) => {
|
|
200
|
-
const files = await parseMultipartBody({
|
|
201
|
-
context: ctx,
|
|
202
|
-
storage: 'disk',
|
|
203
|
-
uploadDir: './uploads',
|
|
204
|
-
});
|
|
78
|
+
- [Utilities Overview](/references/utilities/) - all utility functions
|
|
79
|
+
- [Static Asset Component](/extensions/components/static-asset/) - built-in upload/download CRUD built on this utility
|
|
80
|
+
- [Request Tracker Component](/extensions/components/request-tracker) - `x-request-id` header and request body parsing
|
|
205
81
|
|
|
206
|
-
|
|
207
|
-
{
|
|
208
|
-
message: 'Files uploaded successfully',
|
|
209
|
-
files: files.map(f => ({ name: f.originalname, size: f.size })),
|
|
210
|
-
},
|
|
211
|
-
HTTP.ResultCodes.RS_2.Ok,
|
|
212
|
-
);
|
|
213
|
-
},
|
|
214
|
-
});
|
|
82
|
+
**Files:**
|
|
215
83
|
|
|
216
|
-
|
|
217
|
-
this.bindRoute({
|
|
218
|
-
configs: { path: '/:filename', method: 'get' },
|
|
219
|
-
}).to({
|
|
220
|
-
handler: async (ctx) => {
|
|
221
|
-
const { filename } = ctx.req.valid('param');
|
|
222
|
-
const filePath = path.join('./uploads', filename);
|
|
223
|
-
|
|
224
|
-
// Read file
|
|
225
|
-
const fileStat = fs.statSync(filePath);
|
|
226
|
-
const fileStream = fs.createReadStream(filePath);
|
|
227
|
-
|
|
228
|
-
// Set secure headers
|
|
229
|
-
ctx.header('content-type', 'application/octet-stream');
|
|
230
|
-
ctx.header('content-length', fileStat.size.toString());
|
|
231
|
-
ctx.header('content-disposition', createContentDispositionHeader({
|
|
232
|
-
filename,
|
|
233
|
-
type: 'attachment',
|
|
234
|
-
}));
|
|
235
|
-
ctx.header('x-content-type-options', 'nosniff');
|
|
236
|
-
|
|
237
|
-
return new Response(fileStream, {
|
|
238
|
-
headers: ctx.res.headers,
|
|
239
|
-
status: HTTP.ResultCodes.RS_2.Ok,
|
|
240
|
-
});
|
|
241
|
-
},
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
```
|
|
84
|
+
- [`packages/helpers/src/utilities/request.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/utilities/request.utility.ts)
|
|
@@ -1,162 +1,68 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
The `jsonContent` function creates a standard OpenAPI content object for `application/json` payloads.
|
|
1
|
+
---
|
|
2
|
+
title: Schema Utility
|
|
3
|
+
description: Zod and OpenAPI helpers for JSON response schemas, path params, string constraints, and case conversion
|
|
4
|
+
difficulty: beginner
|
|
5
|
+
lastUpdated: 2026-07-16
|
|
6
|
+
---
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- `opts` (object):
|
|
12
|
-
- `schema` (ZodType): The Zod schema describing the JSON payload.
|
|
13
|
-
- `description` (string): A description of the content.
|
|
14
|
-
- `required` (boolean, optional): Whether the content is required.
|
|
15
|
-
|
|
16
|
-
```typescript
|
|
17
|
-
import { jsonContent } from '@venizia/ignis';
|
|
18
|
-
import { z } from '@hono/zod-openapi';
|
|
19
|
-
|
|
20
|
-
const UserSchema = z.object({
|
|
21
|
-
id: z.number(),
|
|
22
|
-
name: z.string(),
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
const userResponse = {
|
|
26
|
-
description: 'A single user object',
|
|
27
|
-
...jsonContent({ schema: UserSchema, description: 'User data' }),
|
|
28
|
-
};
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## `jsonResponse`
|
|
32
|
-
|
|
33
|
-
The `jsonResponse` function generates a standard OpenAPI response object that includes a success (200 OK) response and a default error response for `4xx | 5xx` status codes. The error response uses the `ErrorSchema`.
|
|
8
|
+
# Schema Utility
|
|
34
9
|
|
|
35
|
-
|
|
10
|
+
Helper functions and predefined schemas for working with `zod` and `@hono/zod-openapi` - building JSON request/response schemas, path-param schemas, and case-converting schemas for API routes.
|
|
36
11
|
|
|
37
|
-
|
|
38
|
-
- `schema` (ZodType): The Zod schema for the success response body.
|
|
39
|
-
- `description` (string, optional): A description for the success response. Defaults to `'Success Response'`.
|
|
40
|
-
- `required` (boolean, optional): Whether the content is required.
|
|
41
|
-
- `headers` (Record<string, THeaderObject>, optional): Custom response headers to include in the success response.
|
|
12
|
+
## In one example
|
|
42
13
|
|
|
43
14
|
```typescript
|
|
44
|
-
import { jsonResponse } from '@venizia/ignis';
|
|
15
|
+
import { jsonResponse, requiredString, idParamsSchema } from '@venizia/ignis';
|
|
45
16
|
import { z } from '@hono/zod-openapi';
|
|
46
17
|
|
|
47
|
-
const UserSchema = z.object({
|
|
48
|
-
id: z.number(),
|
|
49
|
-
name: z.string(),
|
|
50
|
-
});
|
|
18
|
+
const UserSchema = z.object({ id: z.number(), name: requiredString({ min: 1, max: 50 }) });
|
|
51
19
|
|
|
52
20
|
this.defineRoute({
|
|
53
21
|
configs: {
|
|
54
|
-
path: '/',
|
|
22
|
+
path: '/{id}',
|
|
55
23
|
method: 'get',
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
schema: UserSchema,
|
|
59
|
-
}),
|
|
24
|
+
request: { params: idParamsSchema({ idType: 'number' }) },
|
|
25
|
+
responses: jsonResponse({ schema: UserSchema, description: 'A single user object' }),
|
|
60
26
|
},
|
|
61
|
-
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
// With custom headers
|
|
65
|
-
this.defineRoute({
|
|
66
|
-
configs: {
|
|
67
|
-
path: '/list',
|
|
68
|
-
method: 'get',
|
|
69
|
-
responses: jsonResponse({
|
|
70
|
-
schema: z.array(UserSchema),
|
|
71
|
-
description: 'User list',
|
|
72
|
-
headers: {
|
|
73
|
-
'x-total-count': {
|
|
74
|
-
description: 'Total number of records',
|
|
75
|
-
schema: { type: 'string', examples: ['100'] },
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
}),
|
|
27
|
+
handler: (c) => {
|
|
28
|
+
const { id } = c.req.valid('param');
|
|
29
|
+
// ...
|
|
79
30
|
},
|
|
80
|
-
// ...
|
|
81
31
|
});
|
|
82
32
|
```
|
|
83
33
|
|
|
84
|
-
##
|
|
34
|
+
## Functions
|
|
85
35
|
|
|
86
|
-
|
|
36
|
+
| Function | Signature | What it does |
|
|
37
|
+
|----------|-----------|---------------|
|
|
38
|
+
| `jsonContent` | `jsonContent(opts: { schema: ZodType; description: string; required?: boolean }): { description; content; required }` | Builds a standard OpenAPI content object for an `application/json` payload. |
|
|
39
|
+
| `jsonResponse` | `jsonResponse(opts: { schema: ZodType; description?: string; required?: boolean; headers?: Record<string, THeaderObject> }): Record<number \| string, ...>` | Builds a full response map: `200` success (via `jsonContent`, description defaults to `'Success Response'`) plus a `'4xx \| 5xx'` error entry using `ErrorSchema`. |
|
|
40
|
+
| `requiredString` | `requiredString(opts?: { min?: number; max?: number; fixed?: number }): ZodString` | A non-empty (`.nonempty()`) Zod string, optionally constrained by `min`, `max`, or an exact `fixed` length. |
|
|
41
|
+
| `idParamsSchema` | `idParamsSchema(opts?: { idType?: 'number' \| 'string' }): ZodObject` | Builds a path-param schema for `{ id }`, typed and OpenAPI-documented as `number` (default) or `string`. Throws on any other `idType`. |
|
|
42
|
+
| `snakeToCamel` | `snakeToCamel<T extends ZodRawShape>(shape: T): ZodEffects` | Wraps a Zod object `shape` so it accepts `snake_case` input keys and produces a `camelCase`-keyed output, via `.transform()` piped into a camelCase-shaped schema. |
|
|
87
43
|
|
|
88
|
-
|
|
44
|
+
## Predefined schemas and types
|
|
89
45
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
46
|
+
| Export | What it is |
|
|
47
|
+
|--------|------------|
|
|
48
|
+
| `AnyObjectSchema` | `z.object().catchall(z.any())`, OpenAPI-described as `'Unknown schema'` - a permissive object schema. |
|
|
49
|
+
| `TAnyObjectSchema` | Type alias for `z.ZodObject<z.ZodRawShape>`. |
|
|
50
|
+
| `TInferSchema<T>` | Type alias for `z.infer<T>` - the TypeScript type inferred from a Zod schema `T`. |
|
|
94
51
|
|
|
95
|
-
|
|
96
|
-
import { requiredString } from '@venizia/ignis';
|
|
52
|
+
## Notes
|
|
97
53
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
countryCode: requiredString({ fixed: 2 }),
|
|
102
|
-
});
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
## Predefined Schemas
|
|
54
|
+
- **Two source files, one page.** `requiredString`, `AnyObjectSchema`, `TAnyObjectSchema`, and `TInferSchema` live in `schema.utility.ts`; `jsonContent`, `jsonResponse`, `idParamsSchema`, and `snakeToCamel` live in `base/models/common/types.ts`. Both are re-exported from the `@venizia/ignis` root barrel, so the import path is the same either way.
|
|
55
|
+
- **`jsonResponse`'s error branch is fixed** - it always uses `ErrorSchema` under the `'4xx | 5xx'` key; only the success schema, description, and headers are customizable per call.
|
|
56
|
+
- **HTML responses are a separate utility.** For `text/html` routes, use `htmlContent`/`htmlResponse` from the [JSX Utility](/references/utilities/jsx) instead of `jsonContent`/`jsonResponse`.
|
|
106
57
|
|
|
107
|
-
|
|
58
|
+
## See also
|
|
108
59
|
|
|
109
|
-
|
|
60
|
+
- [Utilities Overview](/references/utilities/) - all utility functions
|
|
61
|
+
- [JSX Utility](/references/utilities/jsx) - `htmlContent()` / `htmlResponse()` for HTML routes
|
|
62
|
+
- [Parse Utility](/references/utilities/parse) - `toCamel()` / `keysToCamel()`, the transforms `snakeToCamel` builds on
|
|
63
|
+
- [REST Controllers Guide](/guides/core-concepts/rest-controllers) - defining routes with `request`/`responses`
|
|
110
64
|
|
|
111
|
-
|
|
112
|
-
import { TAnyObjectSchema, TInferSchema } from '@venizia/ignis';
|
|
65
|
+
**Files:**
|
|
113
66
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
type UserType = TInferSchema<typeof UserSchema>;
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## `snakeToCamel`
|
|
121
|
-
|
|
122
|
-
Transforms a Zod object shape from snake_case keys to camelCase. Uses `.transform()` and `.pipe()` to create a schema that accepts snake_case input but produces camelCase output.
|
|
123
|
-
|
|
124
|
-
```typescript
|
|
125
|
-
import { snakeToCamel } from '@venizia/ignis';
|
|
126
|
-
|
|
127
|
-
const schema = snakeToCamel({
|
|
128
|
-
first_name: z.string(),
|
|
129
|
-
last_name: z.string(),
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
// Input: { first_name: 'John', last_name: 'Doe' }
|
|
133
|
-
// Output: { firstName: 'John', lastName: 'Doe' }
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## Custom ID Params
|
|
137
|
-
|
|
138
|
-
Use the `idParamsSchema()` helper to generate path parameter schemas for resource IDs:
|
|
139
|
-
|
|
140
|
-
### `idParamsSchema(opts?)`
|
|
141
|
-
|
|
142
|
-
- `opts` (object, optional):
|
|
143
|
-
- `idType` (string): `'number'` (default) or `'string'`.
|
|
144
|
-
|
|
145
|
-
```typescript
|
|
146
|
-
import { idParamsSchema } from '@venizia/ignis';
|
|
147
|
-
|
|
148
|
-
this.defineRoute({
|
|
149
|
-
configs: {
|
|
150
|
-
path: '/{id}',
|
|
151
|
-
method: 'get',
|
|
152
|
-
request: {
|
|
153
|
-
params: idParamsSchema({ idType: 'number' }),
|
|
154
|
-
},
|
|
155
|
-
// ...
|
|
156
|
-
},
|
|
157
|
-
handler: (c) => {
|
|
158
|
-
const { id } = c.req.valid('param');
|
|
159
|
-
// ...
|
|
160
|
-
},
|
|
161
|
-
});
|
|
162
|
-
```
|
|
67
|
+
- [`packages/core/src/utilities/schema.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/utilities/schema.utility.ts)
|
|
68
|
+
- [`packages/core/src/base/models/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/models/common/types.ts)
|