zod-openapi-share 0.0.1
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/LICENSE +21 -0
- package/README.md +537 -0
- package/dist/index.d.mts +266 -0
- package/dist/index.d.ts +266 -0
- package/dist/index.js +176 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +150 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Yuki Osada
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
# zod-openapi-share
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Myxogastria0808/zod-openapi-share/actions/workflows/test.yaml)
|
|
4
|
+
[](https://github.com/Myxogastria0808/zod-openapi-share/actions/workflows/docs.yaml)
|
|
5
|
+
[](https://www.npmjs.com/package/zod-openapi-share)
|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
[](https://www.npmjs.com/package/zod-openapi-share)
|
|
9
|
+

|
|
10
|
+

|
|
11
|
+

|
|
12
|
+
[](#)
|
|
13
|
+
|
|
14
|
+
`zod-openapi-share` is an extension for `@hono/zod-openapi` that lets you centralize and reuse response definitions across endpoints.
|
|
15
|
+
Normally, `@hono/zod-openapi` requires you to redefine the same responses (e.g., error schemas) for every endpoint, but with `zod-openapi-share`, you can avoid repetition and prevent definition drift, making your backend development using `hono` + `@hono/zod-openapi` cleaner and more consistent.
|
|
16
|
+
|
|
17
|
+
> [!IMPORTANT]
|
|
18
|
+
> Be sure to use the latest version.
|
|
19
|
+
|
|
20
|
+
## What is `zod-openapi-share`?
|
|
21
|
+
|
|
22
|
+
In projects using `hono`, you may have opportunities to use a convenient package called `@hono/zod-openapi` as middleware for generating OpenAPI schemas.
|
|
23
|
+
This package allows you to define both **OpenAPI schemas** and **Zod-based validation** at the same time.
|
|
24
|
+
|
|
25
|
+
However, it has a major drawback: you must repeatedly write out the `responses` definitions for every single status code across all endpoints.
|
|
26
|
+
In many cases, error responses share the exact same structure across endpoints — yet, even if they are identical, you still have to duplicate those definitions.
|
|
27
|
+
|
|
28
|
+
To solve this, `zod-openapi-share` provides a way to **centralize and reuse response definitions** by wrapping around `@hono/zod-openapi`.
|
|
29
|
+
Think of `zod-openapi-share` as an extension package for `@hono/zod-openapi`.
|
|
30
|
+
When using it, you’ll need three packages together: `hono`, `@hono/zod-openapi`, and `zod-openapi-share`.
|
|
31
|
+
|
|
32
|
+
By unifying response definitions, you can develop without worrying about unintended inconsistencies between endpoints.
|
|
33
|
+
If you’re using hono and @hono/zod-openapi, be sure to try **zod-openapi-share**!
|
|
34
|
+
|
|
35
|
+
### before (`hono` + `@hono/zod-openapi`)
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { z, createRoute } from '@hono/zod-openapi';
|
|
39
|
+
|
|
40
|
+
// Commonly Used Response Schema
|
|
41
|
+
const ContentlyStatusCodeArray = [
|
|
42
|
+
100, 102, 103, 200, 201, 202, 203, 206, 207, 208, 226, 300, 301, 302, 303, 305, 306, 307, 308, 400, 401, 402, 403,
|
|
43
|
+
404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 421, 422, 423, 424, 425, 426, 428, 429,
|
|
44
|
+
431, 451, 500, 501, 502, 503, 504, 505, 506, 507, 508, 510, 511, -1,
|
|
45
|
+
] as const;
|
|
46
|
+
|
|
47
|
+
export const errorResponseSchema = z.object({
|
|
48
|
+
status: z.union(ContentlyStatusCodeArray.map((code) => z.literal(code))).meta({
|
|
49
|
+
example: 400,
|
|
50
|
+
description: 'HTTP Status Code',
|
|
51
|
+
}),
|
|
52
|
+
message: z.string().min(1).meta({
|
|
53
|
+
example: 'Bad Request',
|
|
54
|
+
description: 'Error Message',
|
|
55
|
+
}),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Get Request Sample
|
|
59
|
+
const rootGetResponseBodySchema = z.object({
|
|
60
|
+
result: z.string().meta({
|
|
61
|
+
example: 'Hello, World!',
|
|
62
|
+
description: 'Root Endpoint Get Response',
|
|
63
|
+
}),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const rootGetRoute = createRoute({
|
|
67
|
+
path: '/',
|
|
68
|
+
method: 'get',
|
|
69
|
+
description: 'Sample Endpoint',
|
|
70
|
+
responses: {
|
|
71
|
+
200: {
|
|
72
|
+
description: 'OK',
|
|
73
|
+
content: {
|
|
74
|
+
'application/json': {
|
|
75
|
+
schema: rootGetResponseBodySchema,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
//** Despite having the same definition, it must be defined repeatedly for each endpoint! */
|
|
80
|
+
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //
|
|
81
|
+
400: {
|
|
82
|
+
description: 'Bad Request',
|
|
83
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
84
|
+
},
|
|
85
|
+
500: {
|
|
86
|
+
description: 'Internal Server Error',
|
|
87
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
88
|
+
},
|
|
89
|
+
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Post Request Sample
|
|
94
|
+
const rootPostRequestBodySchema = z.object({
|
|
95
|
+
input: z.string().min(1).max(100).meta({
|
|
96
|
+
example: 'Hello, World!',
|
|
97
|
+
description: 'Root Endpoint Post Request',
|
|
98
|
+
}),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const rootPostResponseBodySchema = z.object({
|
|
102
|
+
result: z.string().meta({
|
|
103
|
+
example: 'Hello, World!',
|
|
104
|
+
description: 'Root Endpoint Post Response',
|
|
105
|
+
}),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const rootPostRoute = createRoute({
|
|
109
|
+
path: '/',
|
|
110
|
+
method: 'post',
|
|
111
|
+
description: 'Sample Endpoint',
|
|
112
|
+
request: {
|
|
113
|
+
body: {
|
|
114
|
+
required: true,
|
|
115
|
+
content: {
|
|
116
|
+
'application/json': {
|
|
117
|
+
schema: rootPostRequestBodySchema,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
responses: {
|
|
123
|
+
200: {
|
|
124
|
+
description: 'OK',
|
|
125
|
+
content: {
|
|
126
|
+
'application/json': {
|
|
127
|
+
schema: rootPostResponseBodySchema,
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
//** Despite having the same definition, it must be defined repeatedly for each endpoint! */
|
|
132
|
+
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //
|
|
133
|
+
400: {
|
|
134
|
+
description: 'Bad Request',
|
|
135
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
136
|
+
},
|
|
137
|
+
500: {
|
|
138
|
+
description: 'Internal Server Error',
|
|
139
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
140
|
+
},
|
|
141
|
+
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### after (`hono` + `@hono/zod-openapi` + `zod-openapi-share`)
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { z } from '@hono/zod-openapi';
|
|
151
|
+
import { ZodOpenAPISchema } from 'zod-openapi-share';
|
|
152
|
+
|
|
153
|
+
// Commonly Used Response Schema
|
|
154
|
+
const ContentlyStatusCodeArray = [
|
|
155
|
+
100, 102, 103, 200, 201, 202, 203, 206, 207, 208, 226, 300, 301, 302, 303, 305, 306, 307, 308, 400, 401, 402, 403,
|
|
156
|
+
404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 421, 422, 423, 424, 425, 426, 428, 429,
|
|
157
|
+
431, 451, 500, 501, 502, 503, 504, 505, 506, 507, 508, 510, 511, -1,
|
|
158
|
+
] as const;
|
|
159
|
+
|
|
160
|
+
export const errorResponseSchema = z.object({
|
|
161
|
+
status: z.union(ContentlyStatusCodeArray.map((code) => z.literal(code))).meta({
|
|
162
|
+
example: 400,
|
|
163
|
+
description: 'HTTP Status Code',
|
|
164
|
+
}),
|
|
165
|
+
message: z.string().min(1).meta({
|
|
166
|
+
example: 'Bad Request',
|
|
167
|
+
description: 'Error Message',
|
|
168
|
+
}),
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Shared Responses Using ZodOpenAPISchema
|
|
172
|
+
const route = new ZodOpenAPISchema({
|
|
173
|
+
400: {
|
|
174
|
+
description: 'Bad Request',
|
|
175
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
176
|
+
},
|
|
177
|
+
500: {
|
|
178
|
+
description: 'Internal Server Error',
|
|
179
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
180
|
+
},
|
|
181
|
+
} as const);
|
|
182
|
+
|
|
183
|
+
// Get Request Sample
|
|
184
|
+
const rootGetResponseBodySchema = z.object({
|
|
185
|
+
result: z.string().meta({
|
|
186
|
+
example: 'Hello, World!',
|
|
187
|
+
description: 'Root Endpoint Get Response',
|
|
188
|
+
}),
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const rootGetRoute = route.createSchema(
|
|
192
|
+
{
|
|
193
|
+
path: '/',
|
|
194
|
+
method: 'get',
|
|
195
|
+
description: 'Sample Endpoint',
|
|
196
|
+
responses: {
|
|
197
|
+
200: {
|
|
198
|
+
description: 'OK',
|
|
199
|
+
content: {
|
|
200
|
+
'application/json': {
|
|
201
|
+
schema: rootGetResponseBodySchema,
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
// You only need to describe the status codes of the response definitions shared in the array as the second argument!
|
|
208
|
+
[400, 500]
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
// Post Request Sample
|
|
212
|
+
const rootPostRequestBodySchema = z.object({
|
|
213
|
+
input: z.string().min(1).max(100).meta({
|
|
214
|
+
example: 'Hello, World!',
|
|
215
|
+
description: 'Root Endpoint Post Request',
|
|
216
|
+
}),
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const rootPostResponseBodySchema = z.object({
|
|
220
|
+
result: z.string().meta({
|
|
221
|
+
example: 'Hello, World!',
|
|
222
|
+
description: 'Root Endpoint Post Response',
|
|
223
|
+
}),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const rootPostRoute = route.createSchema(
|
|
227
|
+
{
|
|
228
|
+
path: '/',
|
|
229
|
+
method: 'post',
|
|
230
|
+
description: 'Sample Endpoint',
|
|
231
|
+
request: {
|
|
232
|
+
body: {
|
|
233
|
+
required: true,
|
|
234
|
+
content: {
|
|
235
|
+
'application/json': {
|
|
236
|
+
schema: rootPostRequestBodySchema,
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
responses: {
|
|
242
|
+
200: {
|
|
243
|
+
description: 'OK',
|
|
244
|
+
content: {
|
|
245
|
+
'application/json': {
|
|
246
|
+
schema: rootPostResponseBodySchema,
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
}, // You only need to describe the status codes of the response definitions shared in the array as the second argument!
|
|
252
|
+
[400, 500]
|
|
253
|
+
);
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## How to Use
|
|
257
|
+
|
|
258
|
+
**Examples Here**
|
|
259
|
+
|
|
260
|
+
- cloudflare workers example
|
|
261
|
+
- https://github.com/Myxogastria0808/zod-openapi-share/tree/main/examples/cloudflare-workers/
|
|
262
|
+
|
|
263
|
+
- nodejs example
|
|
264
|
+
- https://github.com/Myxogastria0808/zod-openapi-share/tree/main/examples/nodejs/
|
|
265
|
+
|
|
266
|
+
1. Install Packages
|
|
267
|
+
|
|
268
|
+
```sh
|
|
269
|
+
npm install hono @hono/zod-openapi zod-openapi-share
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
2. Create `Zod` Schema and `ZodOpenAPISchema` Class Instance
|
|
273
|
+
|
|
274
|
+
- Example
|
|
275
|
+
|
|
276
|
+
https://github.com/Myxogastria0808/zod-openapi-share/tree/main/examples/nodejs/src/app/share.ts
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
import { z } from '@hono/zod-openapi';
|
|
280
|
+
import { ZodOpenAPISchema } from 'zod-openapi-share';
|
|
281
|
+
|
|
282
|
+
const ContentlyStatusCodeArray = [
|
|
283
|
+
100, 102, 103, 200, 201, 202, 203, 206, 207, 208, 226, 300, 301, 302, 303, 305, 306, 307, 308, 400, 401, 402, 403,
|
|
284
|
+
404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 421, 422, 423, 424, 425, 426, 428, 429,
|
|
285
|
+
431, 451, 500, 501, 502, 503, 504, 505, 506, 507, 508, 510, 511, -1,
|
|
286
|
+
] as const;
|
|
287
|
+
|
|
288
|
+
// Zod Schema for Error Response
|
|
289
|
+
export const errorResponseSchema = z.object({
|
|
290
|
+
status: z.union(ContentlyStatusCodeArray.map((code) => z.literal(code))).meta({
|
|
291
|
+
example: 400,
|
|
292
|
+
description: 'HTTP Status Code',
|
|
293
|
+
}),
|
|
294
|
+
message: z.string().min(1).meta({
|
|
295
|
+
example: 'Bad Request',
|
|
296
|
+
description: 'Error Message',
|
|
297
|
+
}),
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
export type ErrorResponseSchemaType = z.infer<typeof errorResponseSchema>;
|
|
301
|
+
|
|
302
|
+
// ZodOpenAPISchema Instance
|
|
303
|
+
/**
|
|
304
|
+
* Define Shared Responses Here
|
|
305
|
+
*/
|
|
306
|
+
export const route = new ZodOpenAPISchema({
|
|
307
|
+
400: {
|
|
308
|
+
description: 'Bad Request',
|
|
309
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
310
|
+
},
|
|
311
|
+
500: {
|
|
312
|
+
description: 'Internal Server Error',
|
|
313
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
314
|
+
},
|
|
315
|
+
} as const);
|
|
316
|
+
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
3. Create RouteConfig Type Object
|
|
320
|
+
|
|
321
|
+
- `createSchema` Method
|
|
322
|
+
|
|
323
|
+
When you want to learn how to use `createRoute`,
|
|
324
|
+
please refer to the [@hono/zod-openapi](https://hono.dev/examples/zod-openapi).
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
zodOpenAPISchemaInstance.createSchema(
|
|
328
|
+
createRoute object (@hono/zod-openapi RouteConfig type object),
|
|
329
|
+
StatusCode[] (Array of status codes to be shared from ZodOpenAPISchema instance)
|
|
330
|
+
);
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
- Example
|
|
334
|
+
|
|
335
|
+
https://github.com/Myxogastria0808/zod-openapi-share/tree/main/examples/nodejs/src/app/route.ts
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
import { z } from '@hono/zod-openapi';
|
|
339
|
+
import { route } from './share.js';
|
|
340
|
+
|
|
341
|
+
const responseBodySchema = z.object({
|
|
342
|
+
result: z.string().meta({
|
|
343
|
+
example: 'Hello World!',
|
|
344
|
+
description: 'Sample Endpoint Response',
|
|
345
|
+
}),
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
export const rootRoute = route.createSchema(
|
|
349
|
+
{
|
|
350
|
+
path: '/',
|
|
351
|
+
method: 'get',
|
|
352
|
+
description: 'Sample Endpoint',
|
|
353
|
+
responses: {
|
|
354
|
+
200: {
|
|
355
|
+
description: 'OK',
|
|
356
|
+
content: {
|
|
357
|
+
'application/json': {
|
|
358
|
+
schema: responseBodySchema,
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
[400, 500]
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
4. Insert `root` Variable Into Hono `app` Instance
|
|
370
|
+
|
|
371
|
+
- Example
|
|
372
|
+
|
|
373
|
+
https://github.com/Myxogastria0808/zod-openapi-share/tree/main/examples/nodejs/src/app/index.ts
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
import { OpenAPIHono } from '@hono/zod-openapi';
|
|
377
|
+
import { cors } from 'hono/cors';
|
|
378
|
+
import { HTTPException } from 'hono/http-exception';
|
|
379
|
+
import { type ErrorResponseSchemaType } from './share.js';
|
|
380
|
+
import { rootRoute } from './route.js';
|
|
381
|
+
import { Scalar } from '@scalar/hono-api-reference';
|
|
382
|
+
|
|
383
|
+
export const api = () => {
|
|
384
|
+
const app = new OpenAPIHono({
|
|
385
|
+
// Zod Validation Error Hook
|
|
386
|
+
defaultHook: (result) => {
|
|
387
|
+
if (!result.success) {
|
|
388
|
+
console.error(result.error);
|
|
389
|
+
throw new HTTPException(400, {
|
|
390
|
+
message: 'Zod Validation Error',
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// 404 Not Found Handler
|
|
397
|
+
app.notFound((c) => {
|
|
398
|
+
console.error(`Not Found: ${c.req.url}`);
|
|
399
|
+
return c.json({ status: 404, message: 'Not Found' } satisfies ErrorResponseSchemaType, 404);
|
|
400
|
+
});
|
|
401
|
+
// Other Error Handler
|
|
402
|
+
app.onError((error, c) => {
|
|
403
|
+
if (error instanceof HTTPException) {
|
|
404
|
+
return c.json(
|
|
405
|
+
{
|
|
406
|
+
status: error.status,
|
|
407
|
+
message: error.message,
|
|
408
|
+
} satisfies ErrorResponseSchemaType,
|
|
409
|
+
error.status
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
return c.json(
|
|
413
|
+
{
|
|
414
|
+
status: 500,
|
|
415
|
+
message: 'Internal Server Error',
|
|
416
|
+
} satisfies ErrorResponseSchemaType,
|
|
417
|
+
500
|
|
418
|
+
);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// Settings of CORS
|
|
422
|
+
app.use('*', cors());
|
|
423
|
+
|
|
424
|
+
// OpenAPI Document Endpoint
|
|
425
|
+
app.doc('/openapi', {
|
|
426
|
+
openapi: '3.0.0',
|
|
427
|
+
info: {
|
|
428
|
+
title: 'Echo API',
|
|
429
|
+
version: '1.0.0',
|
|
430
|
+
description: '受け取った入力値をそのまま応答するAPI',
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// Scalar Web UI Endpoint
|
|
435
|
+
// References
|
|
436
|
+
// https://guides.scalar.com/scalar/scalar-api-references/integrations/hono
|
|
437
|
+
app.get('/scalar', Scalar({ url: '/openapi' }));
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Add route to app instance
|
|
441
|
+
*/
|
|
442
|
+
app.openapi(rootRoute, (c) => {
|
|
443
|
+
return c.json({ result: 'Hello World!' });
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
return app;
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
5. Define `serve` (Define Entry Point)
|
|
452
|
+
|
|
453
|
+
- Example
|
|
454
|
+
|
|
455
|
+
https://github.com/Myxogastria0808/zod-openapi-share/tree/main/examples/nodejs/src/index.ts
|
|
456
|
+
|
|
457
|
+
```ts
|
|
458
|
+
import { serve } from '@hono/node-server';
|
|
459
|
+
import { api } from './app/index.js';
|
|
460
|
+
|
|
461
|
+
serve(
|
|
462
|
+
{
|
|
463
|
+
fetch: api().fetch,
|
|
464
|
+
port: 3000,
|
|
465
|
+
},
|
|
466
|
+
(info) => {
|
|
467
|
+
console.log(`Server is running on http://localhost:${info.port}`);
|
|
468
|
+
}
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
6. Define Generate OpenAPI Document Program
|
|
474
|
+
|
|
475
|
+
- Example
|
|
476
|
+
|
|
477
|
+
https://github.com/Myxogastria0808/zod-openapi-share/tree/main/examples/nodejs/src/openapi.ts
|
|
478
|
+
|
|
479
|
+
```ts
|
|
480
|
+
import { api } from './app/index.js';
|
|
481
|
+
import fs from 'node:fs';
|
|
482
|
+
|
|
483
|
+
const docs = api().getOpenAPIDocument({
|
|
484
|
+
openapi: '3.0.0',
|
|
485
|
+
info: {
|
|
486
|
+
title: 'hono + @hono/zod-openapi + zod-openapi-share sample',
|
|
487
|
+
version: '1.0.0',
|
|
488
|
+
description: 'This is a sample project to generate OpenAPI documents with Hono and Zod.',
|
|
489
|
+
},
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
const json = JSON.stringify(docs, null, 2);
|
|
493
|
+
|
|
494
|
+
fs.writeFileSync('./openapi.json', json);
|
|
495
|
+
console.log(json);
|
|
496
|
+
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
7. Add generate openapi.json `scripts` to package.json
|
|
500
|
+
|
|
501
|
+
- Example
|
|
502
|
+
|
|
503
|
+
```json
|
|
504
|
+
{
|
|
505
|
+
"name": "example",
|
|
506
|
+
"type": "module",
|
|
507
|
+
"private": true,
|
|
508
|
+
"scripts": {
|
|
509
|
+
"dev": "tsx watch src/index.ts",
|
|
510
|
+
"build": "tsc",
|
|
511
|
+
"start": "node dist/index.js",
|
|
512
|
+
"openapi": "tsx src/openapi.ts" // Add this script for generating OpenAPI document
|
|
513
|
+
},
|
|
514
|
+
// [Omitted]
|
|
515
|
+
}
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
## HTML Documentation Generated by typedoc
|
|
519
|
+
|
|
520
|
+
https://myxogastria0808.github.io/zod-openapi-share/
|
|
521
|
+
|
|
522
|
+
## Test Coverage Generated by @vitest/coverage-v8
|
|
523
|
+
|
|
524
|
+
https://myxogastria0808.github.io/zod-openapi-share/coverage/
|
|
525
|
+
|
|
526
|
+
## Test Result Generated by @vitest/ui
|
|
527
|
+
|
|
528
|
+
https://myxogastria0808.github.io/zod-openapi-share/html/
|
|
529
|
+
|
|
530
|
+
## DeepWiki
|
|
531
|
+
|
|
532
|
+
> [!WARNING]
|
|
533
|
+
> The accuracy of the contents of generated deepwiki has not been verified by me.
|
|
534
|
+
>
|
|
535
|
+
> I recommend that you look at the documentation at [typedoc](https://myxogastria0808.github.io/zod-openapi-share/).
|
|
536
|
+
|
|
537
|
+
https://deepwiki.com/Myxogastria0808/zod-openapi-share/
|