@kamaalio/hono-standard-openapi
v0.0.20
Published
Generate OpenAPI documents from any Standard Schema library, for Hono.
Maintainers
Readme
hono-standard-openapi
Generate OpenAPI 3.1 documents from Hono routes written with any Standard Schema-compatible validator. Routes validate requests, infer handler types, and generate a matching OpenAPI document.
Index
- Installation
- Schema-library support
- Set up a route
- Define a reusable route
- Add middleware to a route
- Add response examples
- Handle validation errors
- Read the OpenAPI document
- Composing schemas
- Using ArkType
- Using Zod
- Using Valibot
- Using Sury
- Using VineJS
Installation
pnpm add hono @kamaalio/hono-standard-openapiInstall one supported schema library as well. The examples below use Zod:
pnpm add zodSchema-library support
The libraries below are supported for request validation and OpenAPI generation.
| Library | Request validation | OpenAPI schemas | Named components | openapi-3.0 documents | Responses show transforms |
| ------------------------------------------------ | ------------------ | --------------- | ------------------------------- | ----------------------- | -------------------------------------- |
| ArkType | ✅ | ✅ | ✅ | ❌ — throws | ✅ |
| Zod | ✅ | ✅ | ✅ | ✅ | ✅ |
| Zod Mini | ✅ | ✅ | ✅ | ✅ | ✅ |
| Zod (compiled) | ✅ | ✅ | ✅ | ✅ | ✅ |
| Valibot | ✅ | ✅ | ✅ | ✅ | ❌ — shows the value before validation |
| Sury | ✅ | ✅ | ❌ — schemas are emitted inline | ✅ | ❌ — shows the value before validation |
| VineJS | ✅ | ✅ | ❌ — schemas are emitted inline | ✅ | ❌ — shows the value before validation |
openapi-3.0 documents: can the library generate a version: '3.0' document, or only the default
version: '3.1'?
Responses show transforms: some schemas change a value during validation — coercing a string to a number, applying a default. This says whether the response schema in the generated document matches what your handler actually returns (✅), or still describes the value as it looked before validation (❌).
Set up a route
Define the request and response schemas, describe the route with createRoute, then register it
with app.openapi(). The handler receives validated values through c.req.valid().
import { createRoute, StandardOpenAPIHono } from '@kamaalio/hono-standard-openapi';
import { z } from 'zod';
const Card = z.object({ id: z.string(), name: z.string() }).meta({ $id: 'Card' });
const getCard = createRoute({
method: 'get',
path: '/cards/{cardId}',
request: { params: z.object({ cardId: z.string() }) },
responses: {
200: {
description: 'A card',
content: { 'application/json': { schema: Card } },
},
},
});
const app = new StandardOpenAPIHono();
app.openapi(getCard, c => {
const { cardId } = c.req.valid('param');
return c.json({ id: cardId, name: 'Luffy' }, 200);
});Use $id once to make a schema a reusable OpenAPI component. The generated response then refers
to #/components/schemas/Card. See the guides for nested components, multiple responses, errors,
ArkType metadata, and the Valibot converter setup.
Define a reusable route
Use defineOpenAPIRoute to keep a route, its typed handler, and an optional validation hook in one
value. This is useful when route definitions live in separate modules. Register one or more
definitions with app.openapiRoutes():
import { createRoute, defineOpenAPIRoute, StandardOpenAPIHono } from '@kamaalio/hono-standard-openapi';
export const getHealth = defineOpenAPIRoute({
route: createRoute({
method: 'get',
path: '/health',
responses: { 200: { description: 'The service is healthy' } },
}),
handler: c => c.json({ status: 'ok' }, 200),
});
const app = new StandardOpenAPIHono();
app.openapiRoutes([getHealth]);Add middleware to a route
Set middleware on the route to run one Hono middleware function, or an array of them, before
this route's request validation and handler. It affects serving the route only; it is not included
in the OpenAPI document.
const getCard = createRoute({
method: 'get',
path: '/cards/{cardId}',
middleware: [
async (c, next) => {
if (c.req.header('authorization') == null) return c.json({ message: 'Unauthorized' }, 401);
await next();
},
],
request: { params: z.object({ cardId: z.string() }) },
responses: {
200: { description: 'A card' },
401: { description: 'Unauthorized' },
},
});Add response examples
Put an OpenAPI example next to a response media type. This is independent of the schema library,
so it works with ArkType, Zod, Valibot, Sury, and every other supported Standard Schema library:
responses: {
200: {
description: 'A card',
content: {
'application/json': {
schema: Card,
example: { id: 'card-1', name: 'Luffy' },
},
},
},
},The generated document puts it at
paths./cards/{cardId}.get.responses.200.content.application/json.example.
Handle validation errors
Requests use @hono/standard-validator.
Failures return its standard 400 JSON body with success: false, the raw data, and an error
array. Header schema property names must be lowercase because Hono normalizes request headers. Set
defaultHook on the app to return your own error shape consistently across routes:
const app = new StandardOpenAPIHono({
defaultHook: (result, c) =>
result.success ? undefined : c.json({ code: 'INVALID_REQUEST', message: 'Invalid request' }, 400),
});Document that 400 response in each route when it is part of your API contract.
Read the OpenAPI document
Add app.doc() after registering routes. It serves the generated OpenAPI 3.1 JSON at the path you
choose:
app.doc('/openapi.json', {
openapi: '3.1.1',
info: { title: 'Cards API', version: '1.0.0' },
});
export default app;With the app running, retrieve the specification from GET /openapi.json:
curl http://localhost:3000/openapi.jsonUse that JSON endpoint as the input to Swagger UI, Scalar, or an OpenAPI code generator. For code
that needs the document directly, call app.getOpenAPIDocument(config) with the same configuration.
Guides
- Compose schemas: combine several schemas — possibly from different
libraries — into one response with
allOfandobjectSchema. - Use ArkType: native JSON Schema, components, multiple responses, and errors.
- Use Zod: components, multiple responses, and validation errors.
- Use Valibot: converter setup, components, multiple responses, and errors.
- Use Sury: native JSON Schema and request validation.
- Use VineJS: native Standard JSON Schema and request validation.
