@hono/zod-openapi
Advanced tools
Comparing version 0.0.1 to 0.1.0
import * as openapi3_ts_oas30 from 'openapi3-ts/oas30'; | ||
import { RouteConfig, ZodRequestBody, ZodContentObject, ResponseConfig } from '@asteasolutions/zod-to-openapi'; | ||
import { OpenAPIObjectConfig } from '@asteasolutions/zod-to-openapi/dist/v3.0/openapi-generator'; | ||
import { Env, Hono, Input, Handler, Context, TypedResponse } from 'hono'; | ||
import { Env, Hono, Input, Handler, Schema, Context, TypedResponse } from 'hono'; | ||
import { AnyZodObject, z, ZodSchema, ZodError, ZodType } from 'zod'; | ||
@@ -17,4 +17,7 @@ export { z } from 'zod'; | ||
type IsForm<T> = T extends string ? T extends `multipart/form-data${infer _Rest}` | `application/x-www-form-urlencoded${infer _Rest}` ? 'form' : never : never; | ||
type RequestPart<R extends RouteConfig, Part extends string> = Part extends keyof R['request'] ? R['request'][Part] : never; | ||
type RequestPart<R extends RouteConfig, Part extends string> = Part extends keyof R['request'] ? R['request'][Part] : {}; | ||
type InputTypeBase<R extends RouteConfig, Part extends string, Type extends string> = R['request'] extends RequestTypes ? RequestPart<R, Part> extends AnyZodObject ? { | ||
in: { | ||
[K in Type]: z.input<RequestPart<R, Part>>; | ||
}; | ||
out: { | ||
@@ -25,2 +28,5 @@ [K in Type]: z.input<RequestPart<R, Part>>; | ||
type InputTypeJson<R extends RouteConfig> = R['request'] extends RequestTypes ? R['request']['body'] extends ZodRequestBody ? R['request']['body']['content'] extends ZodContentObject ? IsJson<keyof R['request']['body']['content']> extends never ? {} : R['request']['body']['content'][keyof R['request']['body']['content']]['schema'] extends ZodSchema<any> ? { | ||
in: { | ||
json: z.input<R['request']['body']['content'][keyof R['request']['body']['content']]['schema']>; | ||
}; | ||
out: { | ||
@@ -31,2 +37,5 @@ json: z.input<R['request']['body']['content'][keyof R['request']['body']['content']]['schema']>; | ||
type InputTypeForm<R extends RouteConfig> = R['request'] extends RequestTypes ? R['request']['body'] extends ZodRequestBody ? R['request']['body']['content'] extends ZodContentObject ? IsForm<keyof R['request']['body']['content']> extends never ? {} : R['request']['body']['content'][keyof R['request']['body']['content']]['schema'] extends ZodSchema<any> ? { | ||
in: { | ||
form: z.input<R['request']['body']['content'][keyof R['request']['body']['content']]['schema']>; | ||
}; | ||
out: { | ||
@@ -47,3 +56,3 @@ form: z.input<R['request']['body']['content'][keyof R['request']['body']['content']]['schema']>; | ||
constructor(); | ||
openapi: <R extends RouteConfig, I extends Input = InputTypeBase<R, "params", "param"> & InputTypeBase<R, "query", "query"> & InputTypeForm<R> & InputTypeJson<R>>(route: R, handler: Handler<E, R["path"], I, OutputType<R>>, hook?: Hook<I, E, R["path"], OutputType<R>> | undefined) => this; | ||
openapi: <R extends RouteConfig, I extends Input = InputTypeBase<R, "params", "param"> & InputTypeBase<R, "query", "query"> & InputTypeForm<R> & InputTypeJson<R>>(route: R, handler: Handler<E, R["path"], I, OutputType<R>>, hook?: Hook<I, E, R["path"], OutputType<R>> | undefined) => Hono<E, Schema<R["method"], R["path"], I["in"], OutputType<R>>, BasePath>; | ||
getOpenAPIDocument: (config: OpenAPIObjectConfig) => openapi3_ts_oas30.OpenAPIObject; | ||
@@ -50,0 +59,0 @@ doc: (path: string, config: OpenAPIObjectConfig) => void; |
{ | ||
"name": "@hono/zod-openapi", | ||
"version": "0.0.1", | ||
"version": "0.1.0", | ||
"description": "A wrapper class of Hono which supports OpenAPI.", | ||
@@ -5,0 +5,0 @@ "type": "module", |
139
README.md
# Zod OpenAPI Hono | ||
A wrapper class for Hono that supports OpenAPI. With it, you can validate values and types using [Zod](https://zod.dev/) and generate OpenAPI Swagger documentation. | ||
This is based on [Zod to OpenAPI](https://github.com/asteasolutions/zod-to-openapi). | ||
**Zod OpenAPI Hono** is extending Hono to support OpenAPI. | ||
With it, you can validate values and types using [**Zod**](https://zod.dev/) and generate OpenAPI Swagger documentation. | ||
This is based on [**Zod to OpenAPI**](https://github.com/asteasolutions/zod-to-openapi). | ||
For details on creating schemas and defining routes, please refer to this resource. | ||
_This is not a middleware but hosted on this monorepo_ | ||
_This is not a real middleware but hosted on this monorepo._ | ||
## Usage | ||
### Install | ||
### Installation | ||
``` | ||
You can install it via the npm. Should be installed with `hono` and `zod`. | ||
```sh | ||
npm i hono zod @hono/zod-openapi | ||
``` | ||
### Write your application | ||
### Basic Usage | ||
Define schemas: | ||
#### Write your application | ||
First, define schemas with Zod: | ||
```ts | ||
@@ -39,3 +44,3 @@ import { z } from '@hono/zod-openapi' | ||
.object({ | ||
id: z.number().openapi({ | ||
id: z.string().openapi({ | ||
example: 123, | ||
@@ -53,3 +58,3 @@ }), | ||
Create routes: | ||
Next, create routes: | ||
@@ -74,2 +79,59 @@ ```ts | ||
}, | ||
}, | ||
}) | ||
``` | ||
Finally, create the App: | ||
```ts | ||
import { OpenAPIHono } from '@hono/zod-openapi' | ||
const app = new OpenAPIHono() | ||
app.openapi(route, (c) => { | ||
const { id } = c.req.valid('param') | ||
return c.jsonT({ | ||
id, | ||
age: 20, | ||
name: 'Ultra-man', | ||
}) | ||
}) | ||
// OpenAPI document will be served on /doc | ||
app.doc('/doc', { | ||
openapi: '3.0.0', | ||
info: { | ||
version: '1.0.0', | ||
title: 'My API', | ||
}, | ||
}) | ||
``` | ||
### Handling validation errors | ||
You can handle the validation errors the following ways. | ||
Define the schema: | ||
```ts | ||
const ErrorSchema = z.object({ | ||
code: z.number().openapi({ | ||
example: 400, | ||
}), | ||
message: z.string().openapi({ | ||
example: 'Bad Request', | ||
}), | ||
}) | ||
``` | ||
Add the response: | ||
```ts | ||
const route = createRoute({ | ||
method: 'get', | ||
path: '/users/:id', | ||
request: { | ||
params: ParamsSchema, | ||
}, | ||
responses: { | ||
400: { | ||
@@ -81,3 +143,3 @@ content: { | ||
}, | ||
description: 'Error!', | ||
description: 'Return Error!', | ||
}, | ||
@@ -88,9 +150,5 @@ }, | ||
Create the App: | ||
Add the hook: | ||
```ts | ||
import { OpenAPIHono } from '@hono/zod-openapi' | ||
const app = new OpenAPIHono() | ||
app.openapi( | ||
@@ -106,30 +164,59 @@ route, | ||
}, | ||
// Hook | ||
(result, c) => { | ||
if (!result.success) { | ||
const res = c.jsonT( | ||
return c.jsonT( | ||
{ | ||
ok: false, | ||
code: 400, | ||
message: 'Validation Error!', | ||
}, | ||
400 | ||
) | ||
return res | ||
} | ||
} | ||
) | ||
``` | ||
app.doc('/doc', { | ||
openapi: '3.0.0', | ||
info: { | ||
version: '1.0.0', | ||
title: 'My API', | ||
}, | ||
### Middleware | ||
You can use Hono's middleware as same as using Hono because Zod OpenAPI is just extending Hono. | ||
```ts | ||
import { prettyJSON } from 'hono/pretty-json' | ||
//... | ||
app.use('/doc/*', prettyJSON()) | ||
``` | ||
### RPC-mode | ||
Zod OpenAPI Hono supports Hono's RPC-mode. You can create the types for passing Hono Client: | ||
```ts | ||
import { hc } from 'hono/client' | ||
const appRoutes = app.openapi(route, (c) => { | ||
const data = c.req.valid('json') | ||
return c.jsonT({ | ||
id: data.id, | ||
message: 'Success', | ||
}) | ||
}) | ||
const client = hc<typeof appRoutes>('http://localhost:8787/') | ||
``` | ||
## Author | ||
## References | ||
Yusuke Wada <https://github.com/yusukebe> | ||
- [Hono](https://hono.dev/) | ||
- [Zod](https://zod.dev/) | ||
- [Zod to OpenAPI](https://github.com/asteasolutions/zod-to-openapi) | ||
## Authors | ||
- Yusuke Wada <https://github.com/yusukebe> | ||
## License | ||
MIT |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
20029
257
217