@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,574 +1,131 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: JSX/HTML
|
|
3
|
-
description:
|
|
2
|
+
title: JSX/HTML Utility
|
|
3
|
+
description: Render server-side HTML pages with Hono JSX and document them for OpenAPI
|
|
4
4
|
difficulty: beginner
|
|
5
|
-
lastUpdated: 2026-01-03
|
|
6
5
|
---
|
|
7
6
|
|
|
8
7
|
# JSX/HTML Utility
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
IGNIS renders HTML pages server-side with Hono's built-in JSX support, and documents those routes for OpenAPI with `htmlContent()` / `htmlResponse()`.
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
## In one example
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
The smallest real JSX route: a controller that renders a component through `defineJSXRoute`.
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
| `htmlContent()` | Create HTML content configuration | OpenAPI content object |
|
|
19
|
-
| `htmlResponse()` | Create HTML response with error handling | OpenAPI response object |
|
|
20
|
-
|
|
21
|
-
## When to Use
|
|
22
|
-
|
|
23
|
-
Use these utilities when creating routes that:
|
|
24
|
-
- Render HTML pages using Hono JSX
|
|
25
|
-
- Return server-side rendered content
|
|
26
|
-
- Serve HTML documentation or views
|
|
27
|
-
- Generate HTML emails or reports
|
|
28
|
-
|
|
29
|
-
## htmlContent()
|
|
30
|
-
|
|
31
|
-
Creates a standard OpenAPI content object for `text/html` responses.
|
|
32
|
-
|
|
33
|
-
### Signature
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
function htmlContent(opts: {
|
|
37
|
-
description: string;
|
|
38
|
-
required?: boolean;
|
|
39
|
-
}): {
|
|
40
|
-
description: string;
|
|
41
|
-
content: {
|
|
42
|
-
'text/html': {
|
|
43
|
-
schema: ZodString;
|
|
44
|
-
};
|
|
45
|
-
};
|
|
46
|
-
required: boolean;
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### Parameters
|
|
51
|
-
|
|
52
|
-
| Parameter | Type | Required | Default | Description |
|
|
53
|
-
|-----------|------|----------|---------|-------------|
|
|
54
|
-
| `description` | `string` | Yes | - | Description of the HTML content |
|
|
55
|
-
| `required` | `boolean` | No | `false` | Whether the content is required |
|
|
56
|
-
|
|
57
|
-
### Returns
|
|
58
|
-
|
|
59
|
-
Returns an OpenAPI content configuration object with:
|
|
60
|
-
- `description`: The provided description
|
|
61
|
-
- `content`: Content type configuration for `text/html`
|
|
62
|
-
- `required`: Whether the content is required
|
|
63
|
-
|
|
64
|
-
### Example
|
|
65
|
-
|
|
66
|
-
```typescript
|
|
67
|
-
import { htmlContent } from '@venizia/ignis';
|
|
68
|
-
|
|
69
|
-
const pageContent = htmlContent({
|
|
70
|
-
description: 'HTML page content',
|
|
71
|
-
required: true,
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
// Result:
|
|
75
|
-
// {
|
|
76
|
-
// description: 'HTML page content',
|
|
77
|
-
// content: {
|
|
78
|
-
// 'text/html': {
|
|
79
|
-
// schema: z.string().openapi({
|
|
80
|
-
// description: 'HTML content',
|
|
81
|
-
// example: '<!DOCTYPE html><html>...</html>',
|
|
82
|
-
// }),
|
|
83
|
-
// },
|
|
84
|
-
// },
|
|
85
|
-
// required: true,
|
|
86
|
-
// }
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
## htmlResponse()
|
|
91
|
-
|
|
92
|
-
Creates a standard OpenAPI response object for HTML endpoints, including a success (200 OK) HTML response and a JSON error response for `4xx | 5xx` status codes using the `ErrorSchema`.
|
|
93
|
-
|
|
94
|
-
### Signature
|
|
95
|
-
|
|
96
|
-
```typescript
|
|
97
|
-
function htmlResponse(opts: {
|
|
98
|
-
description: string;
|
|
99
|
-
required?: boolean;
|
|
100
|
-
}): {
|
|
101
|
-
200: typeof htmlContent;
|
|
102
|
-
'4xx | 5xx': {
|
|
103
|
-
description: 'Error Response';
|
|
104
|
-
content: {
|
|
105
|
-
'application/json': {
|
|
106
|
-
schema: ErrorSchema;
|
|
107
|
-
};
|
|
108
|
-
};
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### Parameters
|
|
114
|
-
|
|
115
|
-
| Parameter | Type | Required | Default | Description |
|
|
116
|
-
|-----------|------|----------|---------|-------------|
|
|
117
|
-
| `description` | `string` | Yes | - | Description of the successful HTML response |
|
|
118
|
-
| `required` | `boolean` | No | `false` | Whether the content is required |
|
|
119
|
-
|
|
120
|
-
### Returns
|
|
121
|
-
|
|
122
|
-
Returns an OpenAPI responses object with:
|
|
123
|
-
- `200`: Success response with HTML content (via `htmlContent()`)
|
|
124
|
-
- `4xx | 5xx`: Error responses with JSON error schema
|
|
125
|
-
|
|
126
|
-
### Example
|
|
127
|
-
|
|
128
|
-
```typescript
|
|
129
|
-
import { htmlResponse } from '@venizia/ignis';
|
|
130
|
-
|
|
131
|
-
this.defineRoute({
|
|
132
|
-
configs: {
|
|
133
|
-
path: '/dashboard',
|
|
134
|
-
method: 'get',
|
|
135
|
-
responses: htmlResponse({
|
|
136
|
-
description: 'Dashboard HTML page',
|
|
137
|
-
}),
|
|
138
|
-
},
|
|
139
|
-
handler: async (context) => {
|
|
140
|
-
return context.html(
|
|
141
|
-
<html>
|
|
142
|
-
<head>
|
|
143
|
-
<title>Dashboard</title>
|
|
144
|
-
</head>
|
|
145
|
-
<body>
|
|
146
|
-
<h1>Welcome to Dashboard</h1>
|
|
147
|
-
</body>
|
|
148
|
-
</html>
|
|
149
|
-
);
|
|
150
|
-
},
|
|
151
|
-
});
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
## Usage Examples
|
|
156
|
-
|
|
157
|
-
### Basic HTML Route
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
import { BaseRestController, get, htmlResponse } from '@venizia/ignis';
|
|
161
|
-
|
|
162
|
-
export class PageController extends BaseRestController {
|
|
163
|
-
@get({
|
|
164
|
-
configs: {
|
|
165
|
-
path: '/home',
|
|
166
|
-
responses: htmlResponse({
|
|
167
|
-
description: 'Home page HTML',
|
|
168
|
-
}),
|
|
169
|
-
},
|
|
170
|
-
})
|
|
171
|
-
async getHomePage() {
|
|
172
|
-
return this.context.html(
|
|
173
|
-
<html>
|
|
174
|
-
<head>
|
|
175
|
-
<title>Home</title>
|
|
176
|
-
</head>
|
|
177
|
-
<body>
|
|
178
|
-
<h1>Welcome Home</h1>
|
|
179
|
-
</body>
|
|
180
|
-
</html>
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### HTML Email Preview
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
import { BaseRestController, get, htmlResponse, TRouteContext } from '@venizia/ignis';
|
|
190
|
-
import { z } from '@hono/zod-openapi';
|
|
191
|
-
import { HTTP } from '@venizia/ignis-helpers';
|
|
192
|
-
|
|
193
|
-
const EmailRoutes = {
|
|
194
|
-
PREVIEW: {
|
|
195
|
-
method: HTTP.Methods.GET,
|
|
196
|
-
path: '/preview/:templateId',
|
|
197
|
-
request: {
|
|
198
|
-
params: z.object({ templateId: z.string() }),
|
|
199
|
-
},
|
|
200
|
-
responses: htmlResponse({
|
|
201
|
-
description: 'Email template preview',
|
|
202
|
-
}),
|
|
203
|
-
},
|
|
204
|
-
} as const;
|
|
205
|
-
|
|
206
|
-
export class EmailController extends BaseRestController {
|
|
207
|
-
@get({ configs: EmailRoutes.PREVIEW })
|
|
208
|
-
async previewTemplate(c: TRouteContext) {
|
|
209
|
-
const { templateId } = c.req.valid<{ templateId: string }>('param');
|
|
210
|
-
const template = await this.emailService.getTemplate(templateId);
|
|
211
|
-
|
|
212
|
-
return c.html(
|
|
213
|
-
<html>
|
|
214
|
-
<head>
|
|
215
|
-
<title>Email Preview: {template.subject}</title>
|
|
216
|
-
</head>
|
|
217
|
-
<body>
|
|
218
|
-
<div dangerouslySetInnerHTML={{ __html: template.html }} />
|
|
219
|
-
</body>
|
|
220
|
-
</html>
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
### Documentation Page
|
|
227
|
-
|
|
228
|
-
```typescript
|
|
229
|
-
import { BaseRestController, get, htmlResponse, TRouteContext } from '@venizia/ignis';
|
|
230
|
-
import { z } from '@hono/zod-openapi';
|
|
15
|
+
```tsx
|
|
16
|
+
import { BaseRestController, controller, htmlContent, type IControllerOptions } from '@venizia/ignis';
|
|
231
17
|
import { HTTP } from '@venizia/ignis-helpers';
|
|
232
18
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
path: '/
|
|
237
|
-
request: {
|
|
238
|
-
params: z.object({ section: z.string() }),
|
|
239
|
-
},
|
|
240
|
-
responses: htmlResponse({
|
|
241
|
-
description: 'API documentation page',
|
|
242
|
-
}),
|
|
243
|
-
},
|
|
244
|
-
} as const;
|
|
245
|
-
|
|
246
|
-
export class DocsController extends BaseRestController {
|
|
247
|
-
@get({ configs: DocsRoutes.GET_SECTION })
|
|
248
|
-
async getDocumentation(c: TRouteContext) {
|
|
249
|
-
const { section } = c.req.valid<{ section: string }>('param');
|
|
250
|
-
const content = await this.docsService.getSection(section);
|
|
251
|
-
|
|
252
|
-
return c.html(
|
|
253
|
-
<html>
|
|
254
|
-
<head>
|
|
255
|
-
<title>Docs - {content.title}</title>
|
|
256
|
-
<link rel="stylesheet" href="/styles/docs.css" />
|
|
257
|
-
</head>
|
|
258
|
-
<body>
|
|
259
|
-
<nav>
|
|
260
|
-
<a href="/docs/getting-started">Getting Started</a>
|
|
261
|
-
<a href="/docs/api">API Reference</a>
|
|
262
|
-
</nav>
|
|
263
|
-
<main>
|
|
264
|
-
<h1>{content.title}</h1>
|
|
265
|
-
<div dangerouslySetInnerHTML={{ __html: content.html }} />
|
|
266
|
-
</main>
|
|
267
|
-
</body>
|
|
268
|
-
</html>
|
|
269
|
-
);
|
|
19
|
+
@controller({ path: '/' })
|
|
20
|
+
export class ViewController extends BaseRestController {
|
|
21
|
+
constructor(opts: IControllerOptions) {
|
|
22
|
+
super({ ...opts, scope: ViewController.name, path: '/' });
|
|
270
23
|
}
|
|
271
|
-
}
|
|
272
|
-
```
|
|
273
24
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
description: 'Admin dashboard',
|
|
286
|
-
}),
|
|
287
|
-
},
|
|
288
|
-
})
|
|
289
|
-
async getDashboard() {
|
|
290
|
-
const stats = await this.statsService.getAdminStats();
|
|
291
|
-
|
|
292
|
-
return this.context.html(
|
|
293
|
-
<html>
|
|
294
|
-
<head>
|
|
295
|
-
<title>Admin Dashboard</title>
|
|
296
|
-
<script src="/js/dashboard.js" defer />
|
|
297
|
-
</head>
|
|
298
|
-
<body>
|
|
299
|
-
<div class="dashboard">
|
|
300
|
-
<h1>Admin Dashboard</h1>
|
|
301
|
-
<div class="stats">
|
|
302
|
-
<div class="stat-card">
|
|
303
|
-
<h3>Total Users</h3>
|
|
304
|
-
<p>{stats.totalUsers}</p>
|
|
305
|
-
</div>
|
|
306
|
-
<div class="stat-card">
|
|
307
|
-
<h3>Active Sessions</h3>
|
|
308
|
-
<p>{stats.activeSessions}</p>
|
|
309
|
-
</div>
|
|
310
|
-
</div>
|
|
311
|
-
</div>
|
|
312
|
-
</body>
|
|
313
|
-
</html>
|
|
314
|
-
);
|
|
25
|
+
override binding() {
|
|
26
|
+
this.defineJSXRoute({
|
|
27
|
+
configs: {
|
|
28
|
+
path: '/',
|
|
29
|
+
method: 'get',
|
|
30
|
+
responses: {
|
|
31
|
+
[HTTP.ResultCodes.RS_2.Ok]: htmlContent({ description: 'Home page HTML' }),
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
handler: c => c.html(<h1>Welcome to IGNIS</h1>),
|
|
35
|
+
});
|
|
315
36
|
}
|
|
316
37
|
}
|
|
317
38
|
```
|
|
318
39
|
|
|
40
|
+
No separate JSX renderer is registered - the handler builds a JSX tree and hands it to Hono's own `c.html()`.
|
|
319
41
|
|
|
320
|
-
##
|
|
42
|
+
## How it works
|
|
321
43
|
|
|
322
|
-
|
|
44
|
+
- **Hono JSX renders to HTML.** A handler returns `c.html(<Component />)`; Hono's built-in JSX runtime turns the tree into an HTML string. IGNIS adds nothing on top of that renderer.
|
|
45
|
+
- **`defineJSXRoute` is `defineRoute` with an HTML default.** It builds route configs through `getJSXRouteConfigs` instead of `getRouteConfigs`, which merges a default `htmlResponse({ description: 'HTML page' })` under whatever `responses` you declare - your own `200` entry overrides the default description.
|
|
46
|
+
- **`htmlContent()` / `htmlResponse()` document `text/html`.** They mirror `jsonContent()` / `jsonResponse()` (see [Schema Utility](./schema.md)) but for HTML: `htmlContent()` builds one OpenAPI content object, `htmlResponse()` wraps it into a full `200` success response plus a JSON `4xx | 5xx` error response.
|
|
47
|
+
- **The tsconfig switch is required.** Compiling `.tsx` files with Hono's JSX needs `"jsx": "react-jsx"` and `"jsxImportSource": "hono/jsx"` in `tsconfig.json`.
|
|
323
48
|
|
|
324
|
-
|
|
325
|
-
|--------|----------------|-----------------|
|
|
326
|
-
| **Content-Type** | `text/html` | `application/json` |
|
|
327
|
-
| **Schema** | `z.string()` | Custom Zod schema |
|
|
328
|
-
| **Use Case** | HTML pages, JSX rendering | API responses, data |
|
|
329
|
-
| **Example** | HTML document string | JSON object |
|
|
49
|
+
## Common tasks
|
|
330
50
|
|
|
331
|
-
###
|
|
51
|
+
### Build a reusable layout
|
|
332
52
|
|
|
333
|
-
|
|
334
|
-
|--------|------------------|------------------|
|
|
335
|
-
| **Success Type** | `text/html` (200) | `application/json` (200) |
|
|
336
|
-
| **Error Type** | `application/json` (4xx/5xx) | `application/json` (4xx/5xx) |
|
|
337
|
-
| **Use Case** | Web pages | REST APIs |
|
|
53
|
+
Compose pages from a shared layout using `FC` and `PropsWithChildren`, re-exported from `@venizia/ignis-helpers` (sourced from `hono/jsx`).
|
|
338
54
|
|
|
55
|
+
```tsx
|
|
56
|
+
import type { FC, PropsWithChildren } from '@venizia/ignis-helpers';
|
|
339
57
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
path: '/profile/:userId',
|
|
349
|
-
request: { params: z.object({ userId: z.string() }) },
|
|
350
|
-
responses: htmlResponse({ description: 'User profile page' }),
|
|
351
|
-
} as const;
|
|
352
|
-
|
|
353
|
-
@get({ configs: ProfileConfig })
|
|
354
|
-
async getUserProfile(c: TRouteContext) {
|
|
355
|
-
const { userId } = c.req.valid<{ userId: string }>('param');
|
|
356
|
-
const user = await this.userService.getUser(userId);
|
|
357
|
-
return c.html(<UserProfile user={user} />);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Bad: Don't use htmlResponse for API endpoints - use jsonResponse instead
|
|
58
|
+
export const MainLayout: FC<PropsWithChildren<{ title: string }>> = ({ title, children }) => (
|
|
59
|
+
<html>
|
|
60
|
+
<head>
|
|
61
|
+
<title>{title}</title>
|
|
62
|
+
</head>
|
|
63
|
+
<body>{children}</body>
|
|
64
|
+
</html>
|
|
65
|
+
);
|
|
361
66
|
```
|
|
362
67
|
|
|
363
|
-
###
|
|
364
|
-
|
|
365
|
-
```typescript
|
|
366
|
-
const SettingsConfig = {
|
|
367
|
-
method: HTTP.Methods.GET,
|
|
368
|
-
path: '/admin/settings',
|
|
369
|
-
authenticate: { strategies: ['jwt'] },
|
|
370
|
-
responses: htmlResponse({ description: 'Settings page' }),
|
|
371
|
-
} as const;
|
|
372
|
-
|
|
373
|
-
@get({ configs: SettingsConfig })
|
|
374
|
-
async getSettings(c: TRouteContext) {
|
|
375
|
-
return c.html(<SettingsPage />);
|
|
376
|
-
}
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
### 3. Error Handling
|
|
380
|
-
|
|
381
|
-
HTML routes automatically return JSON errors for 4xx/5xx:
|
|
382
|
-
|
|
383
|
-
```typescript
|
|
384
|
-
const ArticleConfig = {
|
|
385
|
-
method: HTTP.Methods.GET,
|
|
386
|
-
path: '/article/:id',
|
|
387
|
-
request: { params: z.object({ id: z.string() }) },
|
|
388
|
-
responses: htmlResponse({ description: 'Article page' }),
|
|
389
|
-
} as const;
|
|
390
|
-
|
|
391
|
-
@get({ configs: ArticleConfig })
|
|
392
|
-
async getArticle(c: TRouteContext) {
|
|
393
|
-
const { id } = c.req.valid<{ id: string }>('param');
|
|
394
|
-
const article = await this.articleService.findById(id);
|
|
68
|
+
### Pass props into a page component
|
|
395
69
|
|
|
396
|
-
|
|
397
|
-
// Returns JSON error: { message: 'Not found', statusCode: 404 }
|
|
398
|
-
throw new NotFoundError('Article not found');
|
|
399
|
-
}
|
|
70
|
+
Page components take a typed props object like any other Hono JSX component.
|
|
400
71
|
|
|
401
|
-
|
|
72
|
+
```tsx
|
|
73
|
+
interface HomePageProps {
|
|
74
|
+
timestamp?: string;
|
|
402
75
|
}
|
|
403
|
-
```
|
|
404
76
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
request: { params: z.object({ slug: z.string() }) },
|
|
412
|
-
responses: htmlResponse({ description: 'Blog post page' }),
|
|
413
|
-
} as const;
|
|
414
|
-
|
|
415
|
-
@get({ configs: BlogConfig })
|
|
416
|
-
async getBlogPost(c: TRouteContext) {
|
|
417
|
-
const { slug } = c.req.valid<{ slug: string }>('param');
|
|
418
|
-
const post = await this.blogService.getBySlug(slug);
|
|
419
|
-
|
|
420
|
-
return c.html(
|
|
421
|
-
<html>
|
|
422
|
-
<head>
|
|
423
|
-
<title>{post.title} | My Blog</title>
|
|
424
|
-
<meta name="description" content={post.excerpt} />
|
|
425
|
-
<meta property="og:title" content={post.title} />
|
|
426
|
-
<meta property="og:description" content={post.excerpt} />
|
|
427
|
-
<meta property="og:image" content={post.coverImage} />
|
|
428
|
-
</head>
|
|
429
|
-
<body>
|
|
430
|
-
<article>
|
|
431
|
-
<h1>{post.title}</h1>
|
|
432
|
-
<div dangerouslySetInnerHTML={{ __html: post.content }} />
|
|
433
|
-
</article>
|
|
434
|
-
</body>
|
|
435
|
-
</html>
|
|
436
|
-
);
|
|
437
|
-
}
|
|
77
|
+
export const HomePage: FC<HomePageProps> = ({ timestamp }) => (
|
|
78
|
+
<MainLayout title="Home">
|
|
79
|
+
<h1>Welcome</h1>
|
|
80
|
+
{timestamp && <p>Rendered at {timestamp}</p>}
|
|
81
|
+
</MainLayout>
|
|
82
|
+
);
|
|
438
83
|
```
|
|
439
84
|
|
|
85
|
+
### Render trusted raw HTML
|
|
440
86
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
IGNIS uses Hono's built-in JSX support. Make sure to configure your `tsconfig.json`:
|
|
87
|
+
Use `dangerouslySetInnerHTML` for content that is already HTML, such as a stored email or blog template - never for unsanitized user input.
|
|
444
88
|
|
|
445
|
-
```
|
|
446
|
-
{
|
|
447
|
-
"compilerOptions": {
|
|
448
|
-
"jsx": "react-jsx",
|
|
449
|
-
"jsxImportSource": "hono/jsx"
|
|
450
|
-
}
|
|
451
|
-
}
|
|
89
|
+
```tsx
|
|
90
|
+
<div dangerouslySetInnerHTML={{ __html: template.html }} />
|
|
452
91
|
```
|
|
453
92
|
|
|
454
|
-
###
|
|
455
|
-
|
|
456
|
-
```typescript
|
|
457
|
-
// components/Layout.tsx
|
|
458
|
-
export const Layout = (props: { title: string; children: any }) => {
|
|
459
|
-
return (
|
|
460
|
-
<html>
|
|
461
|
-
<head>
|
|
462
|
-
<title>{props.title}</title>
|
|
463
|
-
<link rel="stylesheet" href="/styles/main.css" />
|
|
464
|
-
</head>
|
|
465
|
-
<body>
|
|
466
|
-
<header>
|
|
467
|
-
<nav>
|
|
468
|
-
<a href="/">Home</a>
|
|
469
|
-
<a href="/about">About</a>
|
|
470
|
-
</nav>
|
|
471
|
-
</header>
|
|
472
|
-
<main>{props.children}</main>
|
|
473
|
-
<footer>
|
|
474
|
-
<p>© 2026 My App</p>
|
|
475
|
-
</footer>
|
|
476
|
-
</body>
|
|
477
|
-
</html>
|
|
478
|
-
);
|
|
479
|
-
};
|
|
480
|
-
|
|
481
|
-
// controller.ts
|
|
482
|
-
import { Layout } from './components/Layout';
|
|
483
|
-
|
|
484
|
-
@get({
|
|
485
|
-
configs: {
|
|
486
|
-
path: '/',
|
|
487
|
-
responses: htmlResponse({ description: 'Home page' }),
|
|
488
|
-
},
|
|
489
|
-
})
|
|
490
|
-
async getHome() {
|
|
491
|
-
return this.context.html(
|
|
492
|
-
<Layout title="Home">
|
|
493
|
-
<h1>Welcome to My App</h1>
|
|
494
|
-
<p>This is the home page.</p>
|
|
495
|
-
</Layout>
|
|
496
|
-
);
|
|
497
|
-
}
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
## Common Pitfalls
|
|
502
|
-
|
|
503
|
-
### Pitfall 1: Missing HTML Wrapper
|
|
93
|
+
### Give the response a custom description
|
|
504
94
|
|
|
505
|
-
|
|
506
|
-
// Bad: Incomplete HTML
|
|
507
|
-
@get({
|
|
508
|
-
configs: {
|
|
509
|
-
path: '/page',
|
|
510
|
-
responses: htmlResponse({ description: 'Page' }),
|
|
511
|
-
},
|
|
512
|
-
})
|
|
513
|
-
async getPage() {
|
|
514
|
-
return this.context.html(<div>Hello</div>); // Missing <html>, <head>, <body>
|
|
515
|
-
}
|
|
95
|
+
Pass your own `200` entry in `responses` - it overrides the `defineJSXRoute` default description.
|
|
516
96
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
responses: htmlResponse({ description: 'Page' }),
|
|
522
|
-
},
|
|
523
|
-
})
|
|
524
|
-
async getPage() {
|
|
525
|
-
return this.context.html(
|
|
526
|
-
<html>
|
|
527
|
-
<head><title>Page</title></head>
|
|
528
|
-
<body><div>Hello</div></body>
|
|
529
|
-
</html>
|
|
530
|
-
);
|
|
531
|
-
}
|
|
97
|
+
```tsx
|
|
98
|
+
responses: {
|
|
99
|
+
[HTTP.ResultCodes.RS_2.Ok]: htmlContent({ description: 'Dashboard HTML page' }),
|
|
100
|
+
},
|
|
532
101
|
```
|
|
533
102
|
|
|
534
|
-
###
|
|
103
|
+
### Guard a page with authentication
|
|
535
104
|
|
|
536
|
-
|
|
537
|
-
// Bad: HTML response for API
|
|
538
|
-
@get({
|
|
539
|
-
configs: {
|
|
540
|
-
path: '/api/users',
|
|
541
|
-
responses: htmlResponse({ description: 'Users' }),
|
|
542
|
-
},
|
|
543
|
-
})
|
|
544
|
-
async getUsers() {
|
|
545
|
-
return { users: [...] }; // Should return HTML or use jsonResponse
|
|
546
|
-
}
|
|
105
|
+
JSX routes accept the same `authenticate` config as JSON routes.
|
|
547
106
|
|
|
548
|
-
|
|
549
|
-
|
|
107
|
+
```tsx
|
|
108
|
+
this.defineJSXRoute({
|
|
550
109
|
configs: {
|
|
551
|
-
path: '/
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
110
|
+
path: '/admin',
|
|
111
|
+
method: 'get',
|
|
112
|
+
authenticate: { strategies: ['jwt'] },
|
|
113
|
+
responses: {
|
|
114
|
+
[HTTP.ResultCodes.RS_2.Ok]: htmlContent({ description: 'Admin dashboard' }),
|
|
115
|
+
},
|
|
556
116
|
},
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
return { users: await this.userService.findAll() };
|
|
560
|
-
}
|
|
117
|
+
handler: c => c.html(<AdminDashboard />),
|
|
118
|
+
});
|
|
561
119
|
```
|
|
562
120
|
|
|
121
|
+
## See also
|
|
563
122
|
|
|
564
|
-
|
|
123
|
+
- [Full reference](/references/utilities/jsx-reference) - every function, `defineJSXRoute` behavior, and component patterns
|
|
124
|
+
- [Schema Utility](./schema.md) - the JSON-side helpers (`jsonContent`, `jsonResponse`) this pairs with
|
|
125
|
+
- [Controllers](../base/controllers.md) - `defineRoute`/`bindRoute`, `authenticate`/`authorize` config
|
|
126
|
+
- **External:** [Hono JSX Documentation](https://hono.dev/docs/guides/jsx)
|
|
565
127
|
|
|
566
|
-
|
|
567
|
-
- [Schema Utility](./schema.md) - JSON content and response helpers
|
|
568
|
-
- [Controllers](../base/controllers.md) - Defining routes and handlers
|
|
569
|
-
- [OpenAPI Component](/extensions/components/api-reference) - API documentation
|
|
128
|
+
**Files:**
|
|
570
129
|
|
|
571
|
-
-
|
|
572
|
-
|
|
573
|
-
- [OpenAPI Specification](https://swagger.io/specification/)
|
|
574
|
-
- [React JSX (for reference)](https://react.dev/learn/writing-markup-with-jsx)
|
|
130
|
+
- [`packages/core/src/utilities/jsx.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/utilities/jsx.utility.ts) - `htmlContent`, `htmlResponse`
|
|
131
|
+
- [`packages/core/src/base/controllers/rest/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/controllers/rest/base.ts) - `defineJSXRoute`
|