@hono/zod-openapi 0.18.2 → 0.18.4
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 +71 -5
- package/dist/index.d.mts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +12 -2
- package/dist/index.mjs +12 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,6 +52,7 @@ const UserSchema = z
|
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
> [!TIP]
|
|
55
|
+
>
|
|
55
56
|
> `UserSchema` schema will be registered as `"#/components/schemas/User"` refs in the OpenAPI document.
|
|
56
57
|
> If you want to register the schema as referenced components, use `.openapi()` method.
|
|
57
58
|
|
|
@@ -114,6 +115,71 @@ You can start your app just like you would with Hono. For Cloudflare Workers and
|
|
|
114
115
|
export default app
|
|
115
116
|
```
|
|
116
117
|
|
|
118
|
+
> [!IMPORTANT]
|
|
119
|
+
> The request must have the proper `Content-Type` to ensure the validation. For example, if you want to validate a JSON body, the request must have the `Content-Type` to `application/json` in the request. Otherwise, the value of `c.req.valid('json')` will be `{}`.
|
|
120
|
+
>
|
|
121
|
+
> ```ts
|
|
122
|
+
> import { createRoute, z, OpenAPIHono } from '@hono/zod-openapi'
|
|
123
|
+
>
|
|
124
|
+
> const route = createRoute({
|
|
125
|
+
> method: 'post',
|
|
126
|
+
> path: '/books',
|
|
127
|
+
> request: {
|
|
128
|
+
> body: {
|
|
129
|
+
> content: {
|
|
130
|
+
> 'application/json': {
|
|
131
|
+
> schema: z.object({
|
|
132
|
+
> title: z.string(),
|
|
133
|
+
> }),
|
|
134
|
+
> },
|
|
135
|
+
> },
|
|
136
|
+
> },
|
|
137
|
+
> },
|
|
138
|
+
> responses: {
|
|
139
|
+
> 200: {
|
|
140
|
+
> description: 'Success message',
|
|
141
|
+
> },
|
|
142
|
+
> },
|
|
143
|
+
> })
|
|
144
|
+
>
|
|
145
|
+
> const app = new OpenAPIHono()
|
|
146
|
+
>
|
|
147
|
+
> app.openapi(route, (c) => {
|
|
148
|
+
> const validatedBody = c.req.valid('json')
|
|
149
|
+
> return c.json(validatedBody) // validatedBody is {}
|
|
150
|
+
> })
|
|
151
|
+
>
|
|
152
|
+
> const res = await app.request('/books', {
|
|
153
|
+
> method: 'POST',
|
|
154
|
+
> body: JSON.stringify({ title: 'foo' }),
|
|
155
|
+
> // The Content-Type header is lacking.
|
|
156
|
+
> })
|
|
157
|
+
>
|
|
158
|
+
> const data = await res.json()
|
|
159
|
+
> console.log(data) // {}
|
|
160
|
+
> ```
|
|
161
|
+
>
|
|
162
|
+
> If you want to force validation of requests that do not have the proper `Content-Type`, set the value of `request.body.required` to `true`.
|
|
163
|
+
>
|
|
164
|
+
> ```ts
|
|
165
|
+
> const route = createRoute({
|
|
166
|
+
> method: 'post',
|
|
167
|
+
> path: '/books',
|
|
168
|
+
> request: {
|
|
169
|
+
> body: {
|
|
170
|
+
> content: {
|
|
171
|
+
> 'application/json': {
|
|
172
|
+
> schema: z.object({
|
|
173
|
+
> title: z.string(),
|
|
174
|
+
> }),
|
|
175
|
+
> },
|
|
176
|
+
> },
|
|
177
|
+
> required: true, // <== add
|
|
178
|
+
> },
|
|
179
|
+
> },
|
|
180
|
+
> })
|
|
181
|
+
> ```
|
|
182
|
+
|
|
117
183
|
### Handling Validation Errors
|
|
118
184
|
|
|
119
185
|
Validation errors can be handled as follows:
|
|
@@ -241,7 +307,10 @@ You can generate OpenAPI v3.1 spec using the following methods:
|
|
|
241
307
|
|
|
242
308
|
```ts
|
|
243
309
|
app.doc31('/docs', { openapi: '3.1.0', info: { title: 'foo', version: '1' } }) // new endpoint
|
|
244
|
-
app.getOpenAPI31Document({
|
|
310
|
+
app.getOpenAPI31Document({
|
|
311
|
+
openapi: '3.1.0',
|
|
312
|
+
info: { title: 'foo', version: '1' },
|
|
313
|
+
}) // schema object
|
|
245
314
|
```
|
|
246
315
|
|
|
247
316
|
### The Registry
|
|
@@ -285,10 +354,7 @@ const route = createRoute({
|
|
|
285
354
|
request: {
|
|
286
355
|
params: ParamsSchema,
|
|
287
356
|
},
|
|
288
|
-
middleware: [
|
|
289
|
-
prettyJSON(),
|
|
290
|
-
cache({ cacheName: 'my-cache' })
|
|
291
|
-
] as const, // Use `as const` to ensure TypeScript infers the middleware's Context.
|
|
357
|
+
middleware: [prettyJSON(), cache({ cacheName: 'my-cache' })] as const, // Use `as const` to ensure TypeScript infers the middleware's Context.
|
|
292
358
|
responses: {
|
|
293
359
|
200: {
|
|
294
360
|
content: {
|
package/dist/index.d.mts
CHANGED
|
@@ -4,8 +4,8 @@ import { RouteConfig as RouteConfig$1, ZodMediaTypeObject, OpenAPIRegistry, ZodR
|
|
|
4
4
|
export { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
|
|
5
5
|
import { MiddlewareHandler, TypedResponse, Env, ValidationTargets, Context, Input, Handler, Schema, Hono, ToSchema } from 'hono';
|
|
6
6
|
import { MergePath, MergeSchemaPath } from 'hono/types';
|
|
7
|
-
import { RemoveBlankRecord, SimplifyDeepArray, JSONValue, JSONParsed } from 'hono/utils/types';
|
|
8
7
|
import { StatusCode, InfoStatusCode, SuccessStatusCode, RedirectStatusCode, ClientErrorStatusCode, ServerErrorStatusCode } from 'hono/utils/http-status';
|
|
8
|
+
import { RemoveBlankRecord, SimplifyDeepArray, JSONValue, JSONParsed } from 'hono/utils/types';
|
|
9
9
|
import { ZodError, ZodType, z, ZodSchema } from 'zod';
|
|
10
10
|
export { z } from 'zod';
|
|
11
11
|
|
|
@@ -69,9 +69,10 @@ type StatusCodeRangeDefinitions = {
|
|
|
69
69
|
};
|
|
70
70
|
type RouteConfigStatusCode = keyof StatusCodeRangeDefinitions | StatusCode;
|
|
71
71
|
type ExtractStatusCode<T extends RouteConfigStatusCode> = T extends keyof StatusCodeRangeDefinitions ? StatusCodeRangeDefinitions[T] : T;
|
|
72
|
+
type DefinedStatusCodes<R extends RouteConfig> = keyof R['responses'] & RouteConfigStatusCode;
|
|
72
73
|
type RouteConfigToTypedResponse<R extends RouteConfig> = {
|
|
73
|
-
[Status in
|
|
74
|
-
}[keyof R['responses']
|
|
74
|
+
[Status in DefinedStatusCodes<R>]: undefined extends R['responses'][Status]['content'] ? TypedResponse<{}, ExtractStatusCode<Status>, string> : ReturnJsonOrTextOrResponse<keyof R['responses'][Status]['content'], ExtractContent<R['responses'][Status]['content']>, Status>;
|
|
75
|
+
}[DefinedStatusCodes<R>] | ('default' extends keyof R['responses'] ? undefined extends R['responses']['default']['content'] ? TypedResponse<{}, Exclude<StatusCode, ExtractStatusCode<DefinedStatusCodes<R>>>, string> : ReturnJsonOrTextOrResponse<keyof R['responses']['default']['content'], ExtractContent<R['responses']['default']['content']>, Exclude<StatusCode, ExtractStatusCode<DefinedStatusCodes<R>>>> : never);
|
|
75
76
|
type Hook<T, E extends Env, P extends string, R> = (result: {
|
|
76
77
|
target: keyof ValidationTargets;
|
|
77
78
|
} & ({
|
package/dist/index.d.ts
CHANGED
|
@@ -4,8 +4,8 @@ import { RouteConfig as RouteConfig$1, ZodMediaTypeObject, OpenAPIRegistry, ZodR
|
|
|
4
4
|
export { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
|
|
5
5
|
import { MiddlewareHandler, TypedResponse, Env, ValidationTargets, Context, Input, Handler, Schema, Hono, ToSchema } from 'hono';
|
|
6
6
|
import { MergePath, MergeSchemaPath } from 'hono/types';
|
|
7
|
-
import { RemoveBlankRecord, SimplifyDeepArray, JSONValue, JSONParsed } from 'hono/utils/types';
|
|
8
7
|
import { StatusCode, InfoStatusCode, SuccessStatusCode, RedirectStatusCode, ClientErrorStatusCode, ServerErrorStatusCode } from 'hono/utils/http-status';
|
|
8
|
+
import { RemoveBlankRecord, SimplifyDeepArray, JSONValue, JSONParsed } from 'hono/utils/types';
|
|
9
9
|
import { ZodError, ZodType, z, ZodSchema } from 'zod';
|
|
10
10
|
export { z } from 'zod';
|
|
11
11
|
|
|
@@ -69,9 +69,10 @@ type StatusCodeRangeDefinitions = {
|
|
|
69
69
|
};
|
|
70
70
|
type RouteConfigStatusCode = keyof StatusCodeRangeDefinitions | StatusCode;
|
|
71
71
|
type ExtractStatusCode<T extends RouteConfigStatusCode> = T extends keyof StatusCodeRangeDefinitions ? StatusCodeRangeDefinitions[T] : T;
|
|
72
|
+
type DefinedStatusCodes<R extends RouteConfig> = keyof R['responses'] & RouteConfigStatusCode;
|
|
72
73
|
type RouteConfigToTypedResponse<R extends RouteConfig> = {
|
|
73
|
-
[Status in
|
|
74
|
-
}[keyof R['responses']
|
|
74
|
+
[Status in DefinedStatusCodes<R>]: undefined extends R['responses'][Status]['content'] ? TypedResponse<{}, ExtractStatusCode<Status>, string> : ReturnJsonOrTextOrResponse<keyof R['responses'][Status]['content'], ExtractContent<R['responses'][Status]['content']>, Status>;
|
|
75
|
+
}[DefinedStatusCodes<R>] | ('default' extends keyof R['responses'] ? undefined extends R['responses']['default']['content'] ? TypedResponse<{}, Exclude<StatusCode, ExtractStatusCode<DefinedStatusCodes<R>>>, string> : ReturnJsonOrTextOrResponse<keyof R['responses']['default']['content'], ExtractContent<R['responses']['default']['content']>, Exclude<StatusCode, ExtractStatusCode<DefinedStatusCodes<R>>>> : never);
|
|
75
76
|
type Hook<T, E extends Env, P extends string, R> = (result: {
|
|
76
77
|
target: keyof ValidationTargets;
|
|
77
78
|
} & ({
|
package/dist/index.js
CHANGED
|
@@ -190,12 +190,22 @@ var OpenAPIHono = class _OpenAPIHono extends import_hono.Hono {
|
|
|
190
190
|
case "route":
|
|
191
191
|
return this.openAPIRegistry.registerPath({
|
|
192
192
|
...def.route,
|
|
193
|
-
path: (0, import_url.mergePath)(
|
|
193
|
+
path: (0, import_url.mergePath)(
|
|
194
|
+
pathForOpenAPI,
|
|
195
|
+
// @ts-expect-error _basePath is private
|
|
196
|
+
app._basePath,
|
|
197
|
+
def.route.path
|
|
198
|
+
)
|
|
194
199
|
});
|
|
195
200
|
case "webhook":
|
|
196
201
|
return this.openAPIRegistry.registerWebhook({
|
|
197
202
|
...def.webhook,
|
|
198
|
-
path: (0, import_url.mergePath)(
|
|
203
|
+
path: (0, import_url.mergePath)(
|
|
204
|
+
pathForOpenAPI,
|
|
205
|
+
// @ts-expect-error _basePath is private
|
|
206
|
+
app._basePath,
|
|
207
|
+
def.webhook.path
|
|
208
|
+
)
|
|
199
209
|
});
|
|
200
210
|
case "schema":
|
|
201
211
|
return this.openAPIRegistry.register(def.schema._def.openapi._internal.refId, def.schema);
|
package/dist/index.mjs
CHANGED
|
@@ -168,12 +168,22 @@ var OpenAPIHono = class _OpenAPIHono extends Hono {
|
|
|
168
168
|
case "route":
|
|
169
169
|
return this.openAPIRegistry.registerPath({
|
|
170
170
|
...def.route,
|
|
171
|
-
path: mergePath(
|
|
171
|
+
path: mergePath(
|
|
172
|
+
pathForOpenAPI,
|
|
173
|
+
// @ts-expect-error _basePath is private
|
|
174
|
+
app._basePath,
|
|
175
|
+
def.route.path
|
|
176
|
+
)
|
|
172
177
|
});
|
|
173
178
|
case "webhook":
|
|
174
179
|
return this.openAPIRegistry.registerWebhook({
|
|
175
180
|
...def.webhook,
|
|
176
|
-
path: mergePath(
|
|
181
|
+
path: mergePath(
|
|
182
|
+
pathForOpenAPI,
|
|
183
|
+
// @ts-expect-error _basePath is private
|
|
184
|
+
app._basePath,
|
|
185
|
+
def.webhook.path
|
|
186
|
+
)
|
|
177
187
|
});
|
|
178
188
|
case "schema":
|
|
179
189
|
return this.openAPIRegistry.register(def.schema._def.openapi._internal.refId, def.schema);
|