Template
Scaffolds the app entry and one handler file per tag around the generated routes.
import { defineConfig } from 'hono-takibi'
export default defineConfig({
input: 'openapi.yaml',
output: './src/routes.ts',
pathAlias: '@/', // the files import one another as `@/routes`, `@/handlers` instead of relative paths
template: {},
})Generated files:
src/index.ts: app entry that registers every handlersrc/handlers/*.ts: handler stubs, one file per tag
Re-running is safe
Run it again after editing the spec. New operations get a stub, existing handlers keep the code you wrote.
- A handler you moved into another file is regenerated in place.
- When an operation leaves the spec, its handler declaration is removed. The file stays, delete it by hand when it is empty.
- Files under
handlers/are never deleted. Only top-level files are scanned, not subdirectories.
Modes
routeHandler: false (default)
Each handler file creates its own sub-router. The app mounts them with .route().
// 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) => {})// 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 app
TIP
If you split routes into hand-written files, name each sub-router export <fileBasename>Handler, for example sharesHandler in shares.ts. That is the name the generator merges into.
routeHandler: true
Handlers export typed RouteHandler functions. Route registration lives in index.ts.
// 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 { getHealthRouteHandler } from './handlers'
import { getHealthRoute } from './routes'
const app = new OpenAPIHono()
export const api = app.openapi(getHealthRoute, getHealthRouteHandler)
export default app
define: true
Route and handler live together in one defineOpenAPIRoute() call.
export default defineConfig({
input: 'openapi.yaml',
template: { define: true },
})// src/routes/users.ts
export const getUsersIdRoute = defineOpenAPIRoute({
route: createRoute({
method: 'get',
path: '/users/{id}',
request: { params: z.object({ id: z.string() }) },
responses: {
200: { description: 'ok', content: { 'application/json': { schema: UserSchema } } },
},
}),
handler: async (c) => {},
addRoute: true,
})
In this mode output is the app entry (default ./src/index.ts, must be an index.ts path). Routes go to routes/ next to it, and component schemas to components/index.ts.
Splitting the app
split: true exports one group per first path segment, the root / as __root. See Client.
export default defineConfig({
input: 'openapi.yaml',
output: './src/routes.ts',
template: { routeHandler: true, split: true },
})// src/index.ts
const app = new OpenAPIHono()
export const __root = app.openapi(getRoute, getRouteHandler)
export const books = app
.openapi(getBooksRoute, getBooksRouteHandler)
.openapi(postBooksRoute, postBooksRouteHandler)
export default appWorks with routeHandler: true, routeHandler: false and define: true. Re-running keeps your own exports.
