Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Hono Patterns

ASecurity

Use when building APIs on Hono (Cloudflare Workers, Bun, Deno, Node), debugging route ordering, wiring middleware, validating with @hono/zod-validator, returning streaming responses, configuring CORS with credentials, handling errors via app.onError, or composing typed RPC clients. Triggers: c.env binding types, c.header + c.redirect interaction, missing await next() bugs, Set-Cookie not attaching to redirect, route-precedence surprises, JWT middleware setup, hono/client typed RPC end-to-end....

2 stars
0 votes
0 copies
0 views
Added 9/24/2026
toolstypescriptgobashnextjsnodeexpressdebuggingapidatabasebackend

Works with

cliapi

Security Analysis

A100/100

Pro scans all 5 files and shows the line behind each finding

Scanned 9/24/2026

$npx -y skills add curiositech/port-daddy --skill hono-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Hono Patterns?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Hono Patterns
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/curiositech-hono-patterns-port-daddy/badge)](https://www.skillsdirectory.com/skills/curiositech-hono-patterns-port-daddy)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
Files
SKILL.md
---
license: Apache-2.0
name: hono-patterns
allowed-tools: Read,Write,Edit,Bash,Glob,Grep,WebSearch,WebFetch
description: 'Use when building APIs on Hono (Cloudflare Workers, Bun, Deno, Node), debugging route ordering, wiring middleware, validating with @hono/zod-validator, returning streaming responses, configuring CORS with credentials, handling errors via app.onError, or composing typed RPC clients. Triggers: c.env binding types, c.header + c.redirect interaction, missing await next() bugs, Set-Cookie not attaching to redirect, route-precedence surprises, JWT middleware setup, hono/client typed RPC end-to-end. NOT for Express/Fastify/Koa idioms, tRPC/GraphQL paradigms, or Next.js Route Handlers.'
metadata:
  category: Backend & Infrastructure
  tags:
    - hono
    - cloudflare-workers
    - bun
    - edge
    - middleware
    - typescript
  provenance:
    kind: first-party
    owners: [port-daddy]
  pairs-with:
    - skill: htmx-progressive-enhancement
      reason: Hono is a natural server for htmx -- routes return HTML fragments and the HX-Request header decides fragment vs full page
    - skill: websocket-realtime-expert
      reason: Long-lived WebSocket/SSE connections behind Hono routes need the realtime design (backpressure, reconnect) this skill does not cover
    - skill: error-handling-patterns
      reason: app.onError is the framework hook; what a sanitized, typed error taxonomy looks like is the paired skill's domain
  io-contract:
    kind: deliverable
    consumes:
      - kind: api-requirement
        format: markdown
        description: The API surface to build -- routes, auth/cookie flow, streaming needs, CORS constraints -- from a human or another agent.
      - kind: hono-app-plan
        format: json
        description: A structured plan of route ordering, middleware chain, generics, CORS, and streaming settings, matching schemas/hono-patterns-plan.schema.json.
    produces:
      - kind: api-implementation
        format: markdown
        description: The Hono app design -- typed generics, ordered routes, validators, onError, cookie/CORS wiring -- following this skill's patterns.
      - kind: hono-audit-report
        format: json
        description: A deterministic pass/fail audit of the hono-app-plan against this skill's Quality Gates, as produced by scripts/hono_patterns_audit.mjs.
---

# Hono Patterns

Hono is a small router with a strong middleware model and ergonomic typing. Most surprises come from middleware ordering, deferred header semantics, and the `c.env`/`Variables` generic dance.

## When to use

- Designing an API on Workers, Bun, or Deno where Express would be overkill.
- Type-safe end-to-end RPC client (`hono/client`).
- Streaming SSE / NDJSON responses.
- A redirect-driven login flow where Set-Cookie must attach to the response.
- CORS with credentials, where `*` won't work.
- Custom error responses through `app.onError`.

## Core capabilities

### The `Hono` generic — typed bindings and locals

```ts
type Bindings = {
  DB: D1Database;
  KV: KVNamespace;
  JURY_RIG_INTERNAL_TOKEN: string;
};

type Variables = {
  user: { id: string; role: 'admin' | 'member' };
  startedAt: number;
};

const app = new Hono<{ Bindings: Bindings; Variables: Variables }>();
```

Now `c.env.DB` is typed `D1Database`; `c.var.user` is typed; `c.set('user', …)` and `c.get('user')` are checked.

### Route ordering

Hono matches in registration order. Specific before wildcard:

```ts
app.get('/v1/skills/search', searchHandler);   // specific
app.get('/v1/skills/:id', getOneHandler);      // param
app.all('/v1/*', fallback);                    // catch-all LAST
```

Mounting sub-apps:

```ts
const v2 = new Hono<{ Bindings: Bindings }>();
v2.get('/skills', listV2);
app.route('/v2', v2);

// Or basePath for the whole app:
const api = new Hono().basePath('/api');
```

### Middleware

The chain is request → middleware → handler → middleware-after → response. `await next()` is where you switch direction.

```ts
app.use('*', async (c, next) => {
  const start = Date.now();
  await next();
  c.header('Server-Timing', `total;dur=${Date.now() - start}`);
});

app.use('/admin/*', async (c, next) => {
  const session = c.req.header('Cookie');
  if (!session) return c.text('forbidden', 403);
  c.set('user', await loadUser(session));
  await next();
});
```

Forgetting `await next()` is the most common bug. The handler never runs and the request hangs at middleware.

### Validator middleware

```ts
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';

const createSkill = z.object({
  id: z.string().regex(/^[a-z0-9-]+$/).max(80),
  name: z.string().min(1).max(120),
  tags: z.array(z.string()).max(20).optional(),
});

app.post(
  '/v1/skills',
  zValidator('json', createSkill),
  async (c) => {
    const body = c.req.valid('json'); // typed, narrowed
    await c.env.DB.prepare(
      'INSERT INTO skills (id, name, tags) VALUES (?, ?, ?)'
    ).bind(body.id, body.name, JSON.stringify(body.tags ?? [])).run();
    return c.json({ ok: true }, 201);
  },
);
```

Validation slots: `'json' | 'form' | 'query' | 'param' | 'header' | 'cookie'`. Each adds a typed accessor.

### `c.header()` + `c.redirect()` — the deferred-headers model

```ts
app.get('/_internal/login', (c) => {
  const token = c.req.query('token');
  if (token !== c.env.JURY_RIG_INTERNAL_TOKEN) return c.text('forbidden', 403);
  c.header(
    'Set-Cookie',
    `session=${token}; HttpOnly; Secure; SameSite=Lax; Path=/_internal; Max-Age=${30*86400}`,
  );
  return c.redirect('/_internal/dashboard');
});
```

`c.header()` registers a deferred header on the context; the final `Response` (whether from `c.json`, `c.text`, or `c.redirect`) merges those deferred headers. The cookie WILL attach. If a downstream observer says it didn't, the issue is browser-side (SameSite=Strict on a redirect) or upstream (proxy stripping headers), not Hono.

### Streaming

```ts
import { streamSSE } from 'hono/streaming';

app.get('/v1/events', (c) => {
  return streamSSE(c, async (stream) => {
    let id = 0;
    while (!stream.aborted) {
      await stream.writeSSE({ id: String(id++), event: 'tick', data: JSON.stringify({ now: Date.now() }) });
      await stream.sleep(1000);
    }
  });
});
```

`stream.aborted` flips when the client disconnects — check it in any long loop or you'll leak.

For raw bytes/NDJSON:

```ts
import { stream } from 'hono/streaming';

app.get('/v1/dump', (c) => stream(c, async (s) => {
  for await (const row of fetchRows()) {
    await s.write(JSON.stringify(row) + '\n');
  }
}));
```

### Error handling

```ts
import { HTTPException } from 'hono/http-exception';

app.onError((err, c) => {
  if (err instanceof HTTPException) return err.getResponse();
  // Log with the right severity; don't leak internals.
  console.error('unhandled', err);
  return c.json({ error: 'internal' }, 500);
});

// Throw a typed error from anywhere:
throw new HTTPException(429, { message: 'rate limited', res: new Response('go slow', { status: 429 }) });
```

`onError` wraps the whole app. Routes that throw end up here unless an explicit try/catch handles them.

### Cookies via `hono/cookie`

```ts
import { setCookie, getCookie, deleteCookie } from 'hono/cookie';

setCookie(c, 'session', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'Lax',
  path: '/_internal',
  maxAge: 30 * 86400,
});

const session = getCookie(c, 'session');
deleteCookie(c, 'session', { path: '/_internal' });
```

Helper-based cookies are easier to read than raw `Set-Cookie` strings; both work.

### CORS with credentials

```ts
import { cors } from 'hono/cors';

app.use('/v1/*', cors({
  origin: (origin) => origin?.endsWith('.example.com') ? origin : null,
  credentials: true,                    // sends cookies cross-origin
  allowMethods: ['GET', 'POST', 'OPTIONS'],
  allowHeaders: ['Content-Type', 'Authorization'],
  maxAge: 86400,
}));
```

`credentials: true` requires the explicit origin echoed back — `*` won't work and the browser will block.

### Typed RPC client

```ts
// server
const route = app
  .get('/v1/skills/:id', (c) => c.json({ id: c.req.param('id'), name: 'x' }))
  .post('/v1/skills', zValidator('json', createSkill), (c) => c.json({ ok: true as const }));
export type AppType = typeof route;

// client
import { hc } from 'hono/client';
const client = hc<AppType>('https://api.example.com');
const res = await client.v1.skills[':id'].$get({ param: { id: 'foo' } });
const data = await res.json(); // typed { id: string; name: string }
```

The client mirrors the server type tree exactly. Refactor a route → the client breaks at compile time.

## Anti-patterns

### Catch-all before specific routes

**Symptom:** Specific routes return 404 / wrong handler responses.
**Diagnosis:** `app.all('*', …)` registered before specific GETs. First match wins.
**Fix:** Register catch-alls last. Use `app.notFound(handler)` for 404 instead of a wildcard.

### Forgetting `await next()`

**Symptom:** Request hangs or returns nothing; logs show middleware ran but handler didn't.
**Diagnosis:** Middleware returned without awaiting `next()`.
**Fix:** Always `await next()` unless you intentionally short-circuit (auth fail, rate limit). Linting rule helps.

### `c.set` without typed Variables

**Symptom:** `c.get('user')` returns `unknown`; downstream code does string casts.
**Diagnosis:** Variables generic not declared.
**Fix:** Add `Variables` to the `Hono<{Bindings, Variables}>` generic. Now both `set` and `get` are typed.

### Heavy work in middleware

**Symptom:** Every endpoint, even health checks, costs 50ms.
**Diagnosis:** Middleware does DB-loaded user-loading or audit-logging on every path.
**Fix:** Scope middleware narrowly (`app.use('/api/v1/*', loadUser)`), or memoize per-request.

### SameSite=Strict on a redirect-driven login

**Symptom:** Browser shows 302 with Set-Cookie; immediate follow-up request to dashboard returns 403.
**Diagnosis:** Strict drops cookies on cross-context navigation in some browsers, including the auto-followed redirect from the login URL.
**Fix:** `SameSite=Lax` for session cookies that ride a redirect.

### Returning a raw `Response` and expecting middleware to mutate it

**Symptom:** Middleware "after `next()`" can't read body, can't set headers reliably.
**Diagnosis:** The middleware mutates `c.res`, but the handler returned a fresh Response that bypassed it.
**Fix:** Either use `c.res = newResponse;` in the handler, or return through `c.json/c.text/c.html/c.body`.

## Quality gates

- [ ] Every route has a validator (zod) or explicit typed parsing.
- [ ] Catch-all routes registered last; `app.notFound` set.
- [ ] Every middleware that intends to continue calls `await next()`.
- [ ] `Bindings` and `Variables` generics declared on the root app.
- [ ] `app.onError` returns a sanitized response — no stack leaks.
- [ ] CORS with `credentials: true` echoes a typed allowlist of origins, never `*`.
- [ ] Streaming endpoints check `stream.aborted` in any long loop.
- [ ] Cookie helpers used over raw Set-Cookie unless a specific reason.

## Deterministic Audit

Before shipping (or reviewing) a Hono app, write its shape as a JSON plan matching
`schemas/hono-patterns-plan.schema.json` and run it through the deterministic auditor:

```bash
node scripts/hono_patterns_audit.mjs --input examples/sample-input.json
```

`auditHonoPatterns(plan)` (in `scripts/hono_patterns_audit.mjs`) turns this skill's
anti-patterns and Quality Gates into machine-checkable rules over structured fields — no
keyword matching: a middleware that never `await next()`s (the hanging-request bug), a
catch-all registered before specific routes, unvalidated route inputs, an `onError` that
leaks internals, `credentials: true` CORS with a wildcard or blind-echo origin, streaming
loops that never check `stream.aborted`, `SameSite=Strict` on a redirect-driven login, and
undeclared `Bindings`/`Variables` generics. It returns
`{ pass, score, findings, recommendations }` and exits 1 on failure.
`examples/sample-input.json` is a correctly wired app plan (`pass: true`, zero findings).
See `CHANGELOG.md` for the bundle's history.

## NOT for

- **Express/Fastify/Koa** — different middleware models, different ergonomics.
- **tRPC/GraphQL** — different paradigms; Hono's RPC is REST-flavored.
- **Next.js Route Handlers** — `app/api/*/route.ts` is a different framework.
- **Pure node http** — Hono's value is the middleware + typing; if you need neither, drop down. No dedicated skill.
- **Cloudflare Workers platform issues** (binding errors, deploy failures, secret upload) — once it's the platform, → `cloudflare-workers-debugging`.
- **Webhook signature verification on Hono routes** — Hono is the framework, not the protocol. → `webhook-receiver-design` for HMAC, raw-body, idempotency.

Attribution

curiositechcuriositech
View sourceSee grades on GitHubMore from curiositech →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

ucoz-landing-skill

Create and edit uCoz homepage landing pages via MCP: custom templates, hero sections, lead forms, navigation menus, SEO, and responsive layout. Includes a visual design system (style selection, layout/grid, section recipes, typography/spacing, color tokens, component states, icons, modern CSS/JS, motion, imagery, social proof, copy/voice, accessibility). Uses ucoz-mcp tools for templates, site file uploads, and site modules.

107 votes

Paperclip

Interact with the Paperclip control plane API for task coordination and governance. Use when checking assignments, updating issue status, posting comments, delegating work, managing routines, or calling Paperclip API endpoints.

953191 votes

Pptx

Presentation toolkit (.pptx). Create/edit slides, layouts, content, speaker notes, comments, for programmatic presentation creation and modification.

471861 votes

Daw Music

Digital Audio Workstation usage, music composition, interactive music systems, and game audio implementation for immersive soundscapes.

761 votes

Instantly Rdsthomas Mission Control

Instantly.ai cold email outreach API - manage campaigns, leads, accounts, and analytics. Use for cold email automation, lead management, campaign creation/monitoring, and email account warmup.

761 votes
View all in tools →