
Hono Api Scaffolder
Scaffold a production-style Hono REST API with JSON errors, auth middleware stubs, and CRUD route templates validated with Zod.
Overview
Hono API Scaffolder is an agent skill for the Build phase that generates Hono REST API patterns—error handler, auth middleware, and Zod-validated CRUD routes—for solo builders shipping TypeScript edge APIs.
Install
npx skills add https://github.com/jezweb/claude-skills --skill hono-api-scaffolderWhat is this skill?
- Global JSON error handler using Hono HTTPException (no HTML redirects on API routes)
- Bearer auth middleware template with typed Env/Variables and TODO hooks for JWT or API keys
- Copy-paste route module pattern with list, get, create, update, delete and Zod validators
- Typed Hono factory middleware pattern aligned with Cloudflare Workers-style Env bindings
- 3 template modules: error handler, auth middleware, CRUD route file
Adoption & trust: 1.1k installs on skills.sh; 841 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You know you want Hono but keep rewriting the same error JSON, auth stub, and CRUD boilerplate on every new resource.
Who is it for?
Indie builders starting a Hono API on Workers or Node who want typed middleware and Zod validation from day one.
Skip if: Teams that already maintain a shared internal API framework or need GraphQL, tRPC-only, or non-Hono stacks.
When should I use this skill?
Starting or extending a Hono-based HTTP API and you need standard error, auth, and REST route structure.
What do I get? / Deliverables
You get copy-ready TypeScript modules for errors, auth, and resource routes so you can focus on domain logic and wire deploy next.
- errorHandler module
- requireAuth middleware
- resource route file with CRUD + Zod
Recommended Skills
Journey fit
API scaffolding is a core Build activity once you have chosen a stack and need executable backend structure. Backend subphase is the canonical shelf for HTTP route layers, middleware, and API error contracts—not frontend or deploy-only work.
How it compares
A scaffold template skill—not an MCP server or a live database migration tool.
Common Questions / FAQ
Who is hono-api-scaffolder for?
Solo and indie developers using Claude Code, Cursor, or Codex to bootstrap Hono HTTP APIs with consistent JSON errors and validation.
When should I use hono-api-scaffolder?
During Build when you add a new backend service or resource module and need standard middleware and CRUD route files before integrations and tests.
Is hono-api-scaffolder safe to install?
It is procedural scaffolding code only; review the Security Audits panel on this Prism page and inspect generated auth/token handling before production.
SKILL.md
READMESKILL.md - Hono Api Scaffolder
/** * Standard Error Handler * * Returns JSON errors for all routes. * API routes must return JSON, never HTML redirects. */ import type { Context } from 'hono' import { HTTPException } from 'hono/http-exception' export const errorHandler = (err: Error, c: Context) => { if (err instanceof HTTPException) { return c.json({ error: err.message }, err.status) } console.error('Unhandled error:', err) return c.json({ error: 'Internal server error' }, 500) } /** * Auth Middleware Template * * Validates Bearer token and sets userId/role in context. * Customise the token validation logic for your auth system. */ import { createMiddleware } from 'hono/factory' import type { Env } from '../types' type AuthVariables = { userId: string role: string } export const requireAuth = createMiddleware<{ Bindings: Env Variables: AuthVariables }>(async (c, next) => { const token = c.req.header('Authorization')?.replace('Bearer ', '') if (!token) { return c.json({ error: 'Unauthorized' }, 401) } // TODO: Validate token (JWT, session lookup, API key check, etc.) // const decoded = await verifyToken(token, c.env.JWT_SECRET) // c.set('userId', decoded.sub) // c.set('role', decoded.role) await next() }) /** * Route Template — [Resource Name] * * Copy this file to src/routes/[resource].ts and customise. * Includes: list, get, create, update, delete with Zod validation. */ import { Hono } from 'hono' import { zValidator } from '@hono/zod-validator' import { z } from 'zod' import type { Env } from '../types' const app = new Hono<{ Bindings: Env }>() const createSchema = z.object({ name: z.string().min(1).max(100), // Add fields here }) const updateSchema = createSchema.partial() // GET /api/[resource] app.get('/', async (c) => { const db = c.env.DB const { results } = await db.prepare('SELECT * FROM [table] ORDER BY created_at DESC').all() return c.json({ items: results }) }) // GET /api/[resource]/:id app.get('/:id', async (c) => { const id = c.req.param('id') const item = await c.env.DB.prepare('SELECT * FROM [table] WHERE id = ?').bind(id).first() if (!item) return c.json({ error: 'Not found' }, 404) return c.json({ item }) }) // POST /api/[resource] app.post('/', zValidator('json', createSchema), async (c) => { const body = c.req.valid('json') const id = crypto.randomUUID() await c.env.DB.prepare('INSERT INTO [table] (id, name) VALUES (?, ?)').bind(id, body.name).run() return c.json({ item: { id, ...body } }, 201) }) // PUT /api/[resource]/:id app.put('/:id', zValidator('json', updateSchema), async (c) => { const id = c.req.param('id') const body = c.req.valid('json') // ... update logic return c.json({ item: { id, ...body } }) }) // DELETE /api/[resource]/:id app.delete('/:id', async (c) => { const id = c.req.param('id') await c.env.DB.prepare('DELETE FROM [table] WHERE id = ?').bind(id).run() return c.body(null, 204) }) export default app # API Endpoints Documentation Template Use this format when generating API_ENDPOINTS.md for a project. ## Template ```markdown # API Endpoints Base URL: `https://[project].workers.dev` (production) | `http://localhost:5173` (dev) ## Authentication [Describe auth method: Bearer token, session cookie, API key, none] ## Endpoints ### [Resource Name] #### GET /api/[resource] List all [resources]. - **Auth**: [Required/None] - **Query params**: - `page` (number, default: 1) - `limit` (number, default: 20, max: 100) - `search` (string, optional) - **Response 200**: ```json { "[resources]": [...], "total": 42, "page": 1, "limit": 20 } ``` #### GET /api/[resource]/:id Get a single [resource] by ID. - **Auth**: [Required/None] - **Response 200**: `{ "[resource]": { ... } }` - **Response 404**: `{ "error": "Not found" }` #### POST /api/[resource] Create a new [resource]. - **Auth**: Required - **Body**: ```json { "name": "string (required)", "email": "stri