hono-takibi
v0.9.996
Published
Hono Takibi is a code generator from OpenAPI to @hono/zod-openapi
Downloads
1,639
Readme
![]()
Hono Takibi

npm install -D hono-takibiOpenAPI to Hono Code Generator
Hono Takibi generates type-safe Hono code from OpenAPI / TypeSpec specifications.
- OpenAPI schemas to Zod schemas
- @hono/zod-openapi route definitions
- App entry point + handler stubs + test files
- Client library hooks (TanStack Query, SWR, Svelte Query, Vue Query)
- RPC client, mock server, TypeScript types
- API reference docs with hono-cli commands
Quick Start
CLI
npx hono-takibi path/to/input.{yaml,json,tsp} -o path/to/output.tsConfiguration File
Create hono-takibi.config.ts:
import { defineConfig } from 'hono-takibi/config'
export default defineConfig({
input: 'openapi.yaml',
'zod-openapi': {
output: './src/routes.ts',
},
})npx hono-takibiExample
input:
openapi: 3.1.0
info:
title: Hono Takibi API
version: '1.0.0'
paths:
/:
get:
summary: Welcome
description: Returns a welcome message from Hono Takibi.
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Hono Takibi🔥
required:
- messageoutput:
import { createRoute, z } from '@hono/zod-openapi'
export const getRoute = createRoute({
method: 'get',
path: '/',
summary: 'Welcome',
description: 'Returns a welcome message from Hono Takibi.',
responses: {
200: {
description: 'OK',
content: {
'application/json': {
schema: z
.object({
message: z.string().openapi({ example: 'Hono Takibi🔥' }),
})
.openapi({ required: ['message'] }),
},
},
},
},
})Custom Validation Error Messages
Use x-* vendor extensions to customize Zod error messages:
name:
type: string
minLength: 1
x-error-message: 'Name is required'
x-minimum-message: 'Name cannot be empty'// Generated output
z.string({ error: 'Name is required' }).min(1, { error: 'Name cannot be empty' })| Extension | Applies to |
| ----------------------------- | ----------------------------------------------------------------- |
| x-error-message | Schema constructor (z.string(), z.number(), z.enum(), etc.) |
| x-minimum-message | .min(), .gte() |
| x-maximum-message | .max(), .lte() |
| x-size-message | .length() |
| x-pattern-message | .regex() |
| x-multipleOf-message | .multipleOf() |
| x-enum-error-messages | Per-value enum messages ({ "value": "message" }) |
| x-anyOf-message | anyOf |
| x-oneOf-message | oneOf |
| x-not-message | not |
| x-propertyNames-message | propertyNames |
| x-dependentRequired-message | dependentRequired |
Branded Types
Use the x-brand vendor extension to generate Zod branded types, creating nominal types that are structurally identical but semantically distinct:
components:
schemas:
Cat:
type: object
properties:
name:
type: string
required:
- name
x-brand: Cat
Dog:
type: object
properties:
name:
type: string
required:
- name
x-brand: Dog// Generated output
const CatSchema = z.object({ name: z.string() }).brand<'Cat'>().openapi('Cat')
const DogSchema = z.object({ name: z.string() }).brand<'Dog'>().openapi('Dog')Vite Plugin
Watches your OpenAPI spec and hono-takibi.config.ts for changes, then auto-regenerates code on save.
Requires hono-takibi.config.ts in your project root.
// vite.config.ts
import { honoTakibiVite } from 'hono-takibi/vite-plugin'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [honoTakibiVite()],
})
Template & Test Generation
Generate a complete app structure with handler stubs and test files:
export default defineConfig({
input: 'openapi.yaml',
'zod-openapi': {
output: './src/routes.ts',
template: {
test: true,
pathAlias: '@/',
testFramework: 'bun', // "vitest" (default) | "vite-plus" | "bun"
},
},
})This generates:
src/index.ts- App entry point with route registrationssrc/handlers/*.ts- Handler stubs for each resourcesrc/handlers/*.test.ts- Test files with@faker-js/fakermock data (imports fromvitest,vite-plus/test, orbun:test)
Re-running after updating your OpenAPI spec is safe — your hand-written handler logic and test customizations are preserved. Only new routes are added as stubs.
Note: If you remove a path from your OpenAPI spec and re-run, the corresponding handler and test files will be deleted. Make sure to back up or migrate any custom logic before removing API definitions.
Handler Generation Modes
routeHandler: false (default)
Each handler creates its own sub-router and registers routes:
// src/handlers/health.ts
import { OpenAPIHono } from '@hono/zod-openapi'
import { getHealthRoute } from '@/routes'
const app = new OpenAPIHono()
export const healthHandler = app.openapi(getHealthRoute, (c) => {})The app mounts handlers via .route():
// src/index.ts
import { OpenAPIHono } from '@hono/zod-openapi'
import { healthHandler } from './handlers'
const app = new OpenAPIHono()
export const api = app.route('/', healthHandler)
export default approuteHandler: true
Handlers export typed RouteHandler functions, and index.ts centralizes route registration:
// src/handlers/health.ts
import type { RouteHandler } from '@hono/zod-openapi'
import type { getHealthRoute } from '../routes'
export const getHealthRouteHandler: RouteHandler<typeof getHealthRoute> = async (c) => {}// src/index.ts
import { OpenAPIHono } from '@hono/zod-openapi'
import { getHealthRoute } from './routes'
import { getHealthRouteHandler } from './handlers'
const app = new OpenAPIHono()
export const api = app.openapi(getHealthRoute, getHealthRouteHandler)
export default appClient Library Integrations
Supported: TanStack Query, SWR, Svelte Query, Vue Query, RPC Client.
export default defineConfig({
input: 'openapi.yaml',
'zod-openapi': {
output: './src/routes.ts',
exportSchemas: true,
},
'tanstack-query': {
output: './src/tanstack-query',
import: '../client',
split: true,
client: 'client',
},
})Test & Mock Generation
Test Generation
export default defineConfig({
input: 'openapi.yaml',
'zod-openapi': {
output: './src/routes.ts',
},
test: {
output: './src/test.ts',
import: '../index',
testFramework: 'bun', // "vitest" (default) | "vite-plus" | "bun"
},
})Mock Server Generation
export default defineConfig({
input: 'openapi.yaml',
'zod-openapi': {
output: './src/routes.ts',
readonly: true,
},
mock: {
output: './src/mock.ts',
},
})API Reference Docs
Generate API reference Markdown with hono-cli hono request commands that can be run directly without starting a server:
export default defineConfig({
input: 'openapi.yaml',
'zod-openapi': {
output: './src/routes.ts',
readonly: true,
},
docs: {
output: './docs/api.md',
entry: 'src/index.ts',
},
})To generate curl commands instead of hono request:
export default defineConfig({
input: 'openapi.yaml',
'zod-openapi': {
output: './src/routes.ts',
readonly: true,
},
docs: {
output: './docs/api.md',
curl: true,
baseUrl: 'http://localhost:3000',
},
})Full Config Reference
split: true-outputis a directory (many files +index.ts).splitomitted orfalse-outputis a single.tsfile.outputandroutesare mutually exclusive inzod-openapi.
// hono-takibi.config.ts
import { defineConfig } from 'hono-takibi/config'
export default defineConfig({
// OpenAPI spec file (.yaml, .json, or .tsp)
input: 'openapi.yaml',
// Base path prefix for all routes
basePath: '/api',
// oxfmt FormatOptions for generated code output
// @see https://www.npmjs.com/package/oxfmt
// format: {},
// Main code generation (Zod + OpenAPI + Hono)
'zod-openapi': {
// Output: use 'output' for single file, or 'routes' for split mode (mutually exclusive)
output: './src/routes.ts',
readonly: true, // Add 'as const' to generated schemas
// Template generation (app entry point + handler stubs + tests)
template: {
test: true, // Generate test files
routeHandler: false, // false: inline .openapi() (default), true: RouteHandler exports
pathAlias: '@/', // TypeScript path alias for imports
testFramework: 'vitest', // "vitest" (default) | "vite-plus" | "bun" — test import source
},
// Export options (OpenAPI Components Object)
exportSchemas: true,
exportSchemasTypes: true,
exportResponses: true,
exportParameters: true,
exportParametersTypes: true,
exportExamples: true,
exportRequestBodies: true,
exportHeaders: true,
exportHeadersTypes: true,
exportSecuritySchemes: true,
exportLinks: true,
exportCallbacks: true,
exportPathItems: true,
exportMediaTypes: true,
exportMediaTypesTypes: true,
// Split routes into separate files
routes: {
output: './src/routes',
split: true,
import: '@packages/routes', // Custom import path (monorepo support)
},
// Split components into separate files
components: {
schemas: {
output: './src/schemas',
exportTypes: true,
split: true,
import: '../schemas',
},
responses: {
output: './src/responses',
split: true,
import: '../responses',
},
parameters: {
output: './src/parameters',
exportTypes: true,
split: true,
import: '../parameters',
},
examples: {
output: './src/examples',
split: true,
import: '../examples',
},
requestBodies: {
output: './src/requestBodies',
split: true,
import: '../requestBodies',
},
headers: {
output: './src/headers',
exportTypes: true,
split: true,
import: '../headers',
},
securitySchemes: {
output: './src/securitySchemes',
split: true,
import: '../securitySchemes',
},
links: {
output: './src/links',
split: true,
import: '../links',
},
callbacks: {
output: './src/callbacks',
split: true,
import: '../callbacks',
},
pathItems: {
output: './src/pathItems',
split: true,
import: '../pathItems',
},
mediaTypes: {
output: './src/mediaTypes',
exportTypes: true,
split: true,
import: '../mediaTypes',
},
webhooks: {
output: './src/webhooks',
split: true,
import: '../webhooks',
},
},
},
// TypeScript type generation
type: {
output: './src/types.ts',
readonly: true,
},
// RPC client generation
rpc: {
output: './src/rpc',
import: '../client', // Import path for the Hono RPC client
split: true,
client: 'client', // Export name of the client instance
parseResponse: true, // Use parseResponse for type-safe responses
},
// Client library integrations (TanStack Query, SWR, Svelte Query, Vue Query)
'tanstack-query': {
output: './src/tanstack-query',
import: '../client',
split: true,
client: 'client',
},
'svelte-query': {
output: './src/svelte-query',
import: '../client',
split: true,
client: 'client',
},
swr: {
output: './src/swr',
import: '../client',
split: true,
client: 'client',
},
'vue-query': {
output: './src/vue-query',
import: '../client',
split: true,
client: 'client',
},
// Test generation
test: {
output: './src/test.ts',
import: '../index', // Import path for the app instance
testFramework: 'vitest', // "vitest" (default) | "vite-plus" | "bun" — test import source
},
// Mock server generation
mock: {
output: './src/mock.ts',
},
// API reference docs generation
docs: {
output: './docs/api.md',
entry: 'src/index.ts', // App entry point for hono request commands
curl: false, // true: generate curl commands (requires baseUrl), false: hono request (default)
baseUrl: 'http://localhost:3000', // Base URL for curl commands (required when curl: true)
},
})Projects Using Hono Takibi
- resend-local — A local emulator for the Resend email API.
Limitations
This package is in active development and may introduce breaking changes without prior notice.
- Not all OpenAPI features are supported
- Circular references through
allOfmay cause stack overflow in type generation - Some OpenAPI validations may not be perfectly converted to Zod
We strongly recommend:
- Pinning to exact versions in production
- Testing thoroughly when updating versions
- Reviewing generated code after updates
Contributing
We welcome feedback and contributions to improve the tool!
If you find any issues with the generated code or have suggestions for improvements, please:
- Open an issue at GitHub Issues
- Submit a pull request with your improvements
License
Distributed under the MIT License. See LICENSE for more information.
