src/skills/api-framework-hono/SKILL.md
Hono routes, OpenAPI, Zod validation
npx skillsauth add agents-inc/skills api-framework-honoInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Quick Guide: Use Hono with
@hono/zod-openapifor type-safe REST APIs that auto-generate OpenAPI specs. Importzfrom@hono/zod-openapi(NOT fromzod) so.openapi()is available on all schemas. Always includeoperationIdin routes and export theappinstance for spec generation.
<critical_requirements>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST import z from @hono/zod-openapi, NOT from zod -- this gives Zod the .openapi() method)
(You MUST export the app instance for OpenAPI spec generation)
(You MUST include operationId in every route for clean client generation)
</critical_requirements>
Auto-detection: Hono, @hono/zod-openapi, OpenAPIHono, createRoute, Zod schemas with .openapi(), app.route(), createMiddleware, rate limiting, CORS configuration, health checks, hc client, RPC mode, getContext, tryGetContext, contextStorage, some/every/except middleware
When to use:
When NOT to use:
Key patterns covered:
app.route() and OpenAPIHono.openapi() metadatacreateRoute (operationId, tags, responses)hc) with end-to-end type safetysome/every/except) for declarative authDetailed Resources:
Type safety + documentation from code. Zod schemas serve both validation AND OpenAPI spec generation. Single source of truth flows to clients via generated SDKs or Hono's RPC client.
Use Hono + OpenAPI when: Building public/multi-client APIs, need auto-generated documentation, require formal OpenAPI specs, want type-safe validation.
Use simpler approaches when: Internal-only CRUD, no external API consumers, no documentation needs.
</philosophy>Structure routes using app.route() for modularization. Export the app instance for spec generation.
import { OpenAPIHono } from "@hono/zod-openapi";
const app = new OpenAPIHono().basePath("/api");
app.route("/", jobsRoutes);
app.route("/", companiesRoutes);
// REQUIRED: Export app for spec generation
export { app };
Why good: app.route() prevents God files, app export enables build-time spec generation
See examples/core.md for complete setup with framework adapter exports.
Import z from @hono/zod-openapi (not zod). Use .openapi() for schema registration and documentation.
import { z } from "@hono/zod-openapi";
const MIN_SALARY = 0;
const CURRENCY_CODE_LENGTH = 3;
export const SalarySchema = z
.object({
min: z.number().min(MIN_SALARY),
max: z.number().min(MIN_SALARY),
currency: z.string().length(CURRENCY_CODE_LENGTH),
})
.openapi("Salary", {
example: { min: 60000, max: 90000, currency: "EUR" },
});
Why good: importing z from @hono/zod-openapi provides .openapi() automatically, named constants prevent magic number bugs, .openapi("Name") registers as #/components/schemas/Name
See examples/validation.md for complete schema patterns.
Define routes with createRoute and implement with app.openapi(). Always include operationId.
import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
const getJobsRoute = createRoute({
method: "get",
path: "/jobs",
operationId: "getJobs", // Becomes client method name
tags: ["Jobs"],
request: { query: JobsQuerySchema },
responses: {
200: {
description: "List of jobs",
content: { "application/json": { schema: JobsResponseSchema } },
},
},
});
app.openapi(getJobsRoute, async (c) => {
const { country } = c.req.valid("query"); // Type-safe validated params
// ... handler logic
return c.json({ jobs: results }, 200);
});
Why good: operationId becomes clean client method name (getJobs vs get_api_jobs), c.req.valid() enforces schema validation with types
See examples/core.md for list/detail endpoint examples.
Use named error code constants for consistent, machine-parseable error responses.
export const ErrorCodes = {
VALIDATION_ERROR: "validation_error",
NOT_FOUND: "not_found",
UNAUTHORIZED: "unauthorized",
INTERNAL_ERROR: "internal_error",
} as const;
Why good: Named codes enable frontend switch handling, consistent shape across all endpoints
See examples/error-handling.md for the full handleRouteError utility.
Always specify the alg option on JWT/JWK middleware to prevent algorithm confusion attacks (CVE-2026-22817, CVE-2026-22818, patched in v4.11.4+).
import { verify } from "hono/jwt";
const JWT_ALGORITHM = "HS256";
const payload = await verify(token, secret, JWT_ALGORITHM);
Why good: explicit algorithm prevents attackers from switching to symmetric verification with known public keys
See examples/middleware.md for complete auth middleware with type-safe variables.
</patterns><red_flags>
High Priority:
z from "zod" instead of "@hono/zod-openapi" -- .openapi() won't be availableoperationId in routes -- generated client has ugly method namesapp instance -- can't generate OpenAPI spec at build timealg option -- algorithm confusion vulnerability (CVE-2026-22817/22818)Medium Priority:
c.req.param() / c.req.query() instead of c.req.valid() -- bypasses Zod validation"*") with credentials: true -- browsers reject this (spec violation)Gotchas & Edge Cases:
c.req.valid("param") uses singular "param", not "params" -- easy to mistype.openapi(r1, h1).openapi(r2, h2)) for type inference -- separate calls break ittsconfig.json need "strict": true for RPC type inferencecontextStorage() middleware must be registered before any code calls getContext()tryGetContext() (v4.11.0+) in code that may run outside request context (tests, background jobs)next() never throws in Hono -- wrapping await next() in try/catch is unnecessarygetConnInfo is adapter-specific -- import from hono/bun, hono/deno, @hono/node-server/conninfo, etc. (NOT from hono/ip-restriction)</red_flags>
<critical_reminders>
All code must follow project conventions in CLAUDE.md
(You MUST import z from @hono/zod-openapi, NOT from zod -- this gives Zod the .openapi() method)
(You MUST export the app instance for OpenAPI spec generation)
(You MUST include operationId in every route for clean client generation)
Failure to follow these rules will break OpenAPI spec generation and type safety.
</critical_reminders>
development
Material Design component library for Vue 3
development
VitePress 1.x — Vue-powered static site generator for documentation sites, built on Vite
tools
Docusaurus 3.x documentation framework — site configuration, docs/blog plugins, sidebars, versioning, MDX, swizzling, and deployment
development
TanStack Form patterns - useForm, form.Field, validators, arrays, linked fields, createFormHook, type safety