@sdk-it/hono 0.5.1 → 0.6.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 +203 -0
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# @sdk-it/hono
|
|
2
|
+
|
|
3
|
+
Hono framework integration for SDK-IT that provides type-safe request validation and standardized response handling.
|
|
4
|
+
|
|
5
|
+
To learn more about SDK code generation, see the [TypeScript Doc](../typescript/readme.md)
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @sdk-it/{hono,generic}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Runtime Primitives
|
|
14
|
+
|
|
15
|
+
You can use these functions without the SDK-IT code generation tools, they're completely separate and functional on their own.
|
|
16
|
+
|
|
17
|
+
### Validator Middleware
|
|
18
|
+
|
|
19
|
+
The validator middleware offers type-safe request validation using [Zod](https://github.com/colinhacks/zod) schemas. It automatically validates incoming requests against your defined schemas and provides typed inputs to your handlers.
|
|
20
|
+
|
|
21
|
+
> ![IMPORTANT]
|
|
22
|
+
> For openapi generation to work correctly, you must use the `validate` middleware for each route.
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { validate } from '@sdk-it/hono';
|
|
26
|
+
|
|
27
|
+
app.post(
|
|
28
|
+
'/books',
|
|
29
|
+
validate((payload) => ({
|
|
30
|
+
// Query parameter validation
|
|
31
|
+
page: {
|
|
32
|
+
select: payload.query.page,
|
|
33
|
+
against: z.number().min(1).default(1),
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
// Multiple query parameters (array)
|
|
37
|
+
categories: {
|
|
38
|
+
select: payload.queries.category,
|
|
39
|
+
against: z.array(z.string()),
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
// Body property validation
|
|
43
|
+
title: {
|
|
44
|
+
select: payload.body.title,
|
|
45
|
+
against: z.string().min(1),
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
author: {
|
|
49
|
+
select: payload.body.author,
|
|
50
|
+
against: z.string().min(1),
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
// For nested objects in body
|
|
54
|
+
metadata: {
|
|
55
|
+
select: payload.body.metadata,
|
|
56
|
+
against: z.object({
|
|
57
|
+
isbn: z.string(),
|
|
58
|
+
publishedYear: z.number(),
|
|
59
|
+
}),
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// URL parameter validation
|
|
63
|
+
userId: {
|
|
64
|
+
select: payload.params.userId,
|
|
65
|
+
against: z.string().uuid(),
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
// Header validation
|
|
69
|
+
apiKey: {
|
|
70
|
+
select: payload.headers['x-api-key'],
|
|
71
|
+
against: z.string().min(32),
|
|
72
|
+
},
|
|
73
|
+
})),
|
|
74
|
+
(c) => {
|
|
75
|
+
// TypeScript knows the shape of all inputs
|
|
76
|
+
const { page, categories, title, author, metadata, userId, apiKey } =
|
|
77
|
+
c.var.input;
|
|
78
|
+
return c.json({ success: true });
|
|
79
|
+
},
|
|
80
|
+
);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Response Helper
|
|
84
|
+
|
|
85
|
+
The output function provides a clean API for sending HTTP responses with proper status codes and content types. It automatically handles JSON serialization and content type headers.
|
|
86
|
+
|
|
87
|
+
> ![NOTE]
|
|
88
|
+
> You don't necessarily need to use this function for OpenAPI generation, but it provides a clean and consistent way to send responses.
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { createOutput } from '@sdk-it/hono';
|
|
92
|
+
|
|
93
|
+
const output = createOutput(() => c);
|
|
94
|
+
|
|
95
|
+
// Success responses
|
|
96
|
+
output.ok({ data: 'success' });
|
|
97
|
+
output.accepted({ status: 'processing' });
|
|
98
|
+
|
|
99
|
+
// Error responses
|
|
100
|
+
output.badRequest({ error: 'Invalid input' });
|
|
101
|
+
output.unauthorized({ error: 'Not authenticated' });
|
|
102
|
+
output.forbidden({ error: 'Not authorized' });
|
|
103
|
+
output.notImplemented({ error: 'Coming soon' });
|
|
104
|
+
|
|
105
|
+
// Redirects
|
|
106
|
+
output.redirect('/new-location');
|
|
107
|
+
|
|
108
|
+
// Custom headers
|
|
109
|
+
output.ok({ data: 'success' }, { 'Cache-Control': 'max-age=3600' });
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## OpenAPI Generation
|
|
113
|
+
|
|
114
|
+
SDK-IT relies on the aforementioned primitives and JSDoc tags to correctly infer each route specification.
|
|
115
|
+
|
|
116
|
+
Consider the following example:
|
|
117
|
+
|
|
118
|
+
- Create hono routes with the `@openapi` tag and validate middleware.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import z from 'zod';
|
|
122
|
+
|
|
123
|
+
import { validate } from '@sdk-it/hono';
|
|
124
|
+
|
|
125
|
+
const app = new Hono();
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @openapi listBooks
|
|
129
|
+
* @tags books
|
|
130
|
+
*/
|
|
131
|
+
app.get(
|
|
132
|
+
'/books',
|
|
133
|
+
validate((payload) => ({
|
|
134
|
+
author: {
|
|
135
|
+
select: payload.query.author,
|
|
136
|
+
against: z.string(),
|
|
137
|
+
},
|
|
138
|
+
})),
|
|
139
|
+
async (c) => {
|
|
140
|
+
const books = [{ name: 'OpenAPI' }];
|
|
141
|
+
return c.json(books);
|
|
142
|
+
},
|
|
143
|
+
);
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- Use the generate fn to create an OpenAPI spec from your routes.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import { join } from 'node:path';
|
|
150
|
+
|
|
151
|
+
import { analyze } from '@sdk-it/generic';
|
|
152
|
+
// Use responseAnalyzer from `@sdk-it/hono`
|
|
153
|
+
// only if you use hono context object to send response
|
|
154
|
+
// e.g. c.json({ data: 'success' });
|
|
155
|
+
import { responseAnalyzer } from '@sdk-it/hono';
|
|
156
|
+
// Use responseAnalyzer from `@sdk-it/generic`
|
|
157
|
+
// only if you use the output function to send response
|
|
158
|
+
// e.g. output.ok({ data: 'success' });
|
|
159
|
+
// import { responseAnalyzer } from '@sdk-it/generic';
|
|
160
|
+
|
|
161
|
+
import { generate } from '@sdk-it/typescript';
|
|
162
|
+
|
|
163
|
+
const { paths, components } = await analyze('apps/backend/tsconfig.app.json', {
|
|
164
|
+
responseAnalyzer,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Now you can use the generated specification to create an SDK or save it to a file
|
|
168
|
+
const spec = {
|
|
169
|
+
info: {
|
|
170
|
+
title: 'My API',
|
|
171
|
+
version: '1.0.0',
|
|
172
|
+
},
|
|
173
|
+
paths,
|
|
174
|
+
components,
|
|
175
|
+
};
|
|
176
|
+
await generate(spec, {
|
|
177
|
+
output: join(process.cwd(), './client'),
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
> [!TIP]
|
|
182
|
+
> See [typescript](../typescript/README.md) for more info.
|
|
183
|
+
|
|
184
|
+
- Use the client
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import { Client } from './client';
|
|
188
|
+
|
|
189
|
+
const client = new Client({
|
|
190
|
+
baseUrl: 'http://localhost:3000',
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const [books, error] = await client.request('GET /books', {
|
|
194
|
+
author: 'John Doe',
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Check for errors
|
|
198
|
+
if (error) {
|
|
199
|
+
console.error('Error fetching books:', error);
|
|
200
|
+
} else {
|
|
201
|
+
console.log('Books retrieved:', books);
|
|
202
|
+
}
|
|
203
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdk-it/hono",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"!**/*.tsbuildinfo"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@sdk-it/core": "0.
|
|
24
|
+
"@sdk-it/core": "0.6.0",
|
|
25
25
|
"debug": "^4.4.0",
|
|
26
26
|
"hono": "^4.7.4",
|
|
27
27
|
"typescript": "^5.7.2",
|